LoicPenn LoicPenn - 21 days ago 9
Javascript Question

Javascript regex for Twig templates contents

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

Answer

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

Comments