2

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;
}
asked Nov 13, 2016 at 11:33
3
  • 1
    Why not just {{(.*?)}}? Commented Nov 13, 2016 at 11:42
  • even better without the ? as {{(.*)}} less steps when it's greedy! I think there's no need to be lazy in this case. Commented Nov 13, 2016 at 12:04
  • When i use "all" selector, i get a strange result with the sample content i just added to my question. I get two results, fetching both first and second tag. Commented Nov 13, 2016 at 12:11

1 Answer 1

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}`);
 });
}

answered Nov 13, 2016 at 11:57
Sign up to request clarification or add additional context in comments.

1 Comment

Hm, almost. If you try your code with my string, the first result is unaccurate and catches two first keywords in one string. I'am adding the sample string to my question now.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.