I use a Rich Text Editor to allow online modification of Twig templates. Twig is a template engine using the following syntax to display variables:
{{ object.property }}
It also uses functions to generate URLS, such as:
{{ url('mr_message_read', {id: message.id}) }}
Now what i want is to display, next to my editor, the list of TWIG variables and functions used in the template. For this, i retrieve the current content as HTML with twig "keywords" as shown above. To extract keywords, i use the regex below:
var reg = /{{[^}]+}}/g;
var match = text.match(reg);
console.log( match );
This will work work example 1 but not for example 2, as the Twig function requires the } string. So, i tried several other syntaxes to allow "anything except }}". None of them seem fit:
var reg = /{{[^}]+}}/g; // GOOD but ignores second example
var reg = /{{[^}}]+}}/g; // Idem
var reg = /{{[^}}]*}}/g; // Idem
var reg = /{{(^}}+)}}/; // null
var reg = /{{(^}})+}}/; // null
var reg = /\{\{[^\}\}]+\}\}/g; // Ignores second example
var reg = /\{\{[^}}]+\}\}/g; // Ignores second example
var reg = /\{\{[^\}\}]+\}\}/g; // Ignores second example
var reg = /\{\{[^[}}]]+\}\}/g; // Ignores second example
I'm struggling now. I guess it's an escaping issue, but i'm stuck :)
Sample content :
<p>{{ author.fullname }} wrote you a message. Read it here: <a href="{{ url('mr_message_read', {id: message.id}) }}">Messagerie</a>.</p>
<hr />
<blockquote>
<p>{{ message.content|nl2br }}</p>
</blockquote>
EDIT: My solution based on Thomas code
function getTwigTags(){
var str = CKEDITOR.instances['form_content'].getData();
var regex = /{{\s*([^{]*{([^{]*):\s*(.*?)}.*?|[^{]*)\s*}}/g;
var keywords = new Array();
let m;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
if(match !== 'undefined' && groupIndex == 1)
// console.log(`Found match: group ${groupIndex}: ${match}`);
keywords[keywords.length] = match;
});
}
return keywords;
}
1 Answer 1
You can use this code:
const regex = /{{\s*([^{]*{([^{]*):\s*(.*?)}.*?|[^{]*)\s*}}/g;
const str = `{{ url('mr_message_read', {id: message.id}) }}
{{ object.property }}`;
let m;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
if(match !== 'undefined' && groupIndex > 0)
console.log(`Found match: group ${groupIndex}: ${match}`);
});
}
{{(.*?)}}??as{{(.*)}}less steps when it's greedy! I think there's no need to be lazy in this case.