import CodeMirror from 'codemirror'; CodeMirror.defineMode('nunjucks', (config, parserConfig) => { const baseMode = CodeMirror.getMode(config, parserConfig.baseMode || 'text/plain'); const nunjucksMode = _nunjucksMode(); return CodeMirror.overlayMode(baseMode, nunjucksMode, false); }); function _nunjucksMode () { const regexVariable = /^{{\s*([^ }]+)\s*[^}]*\s*}}/; const regexTag = /^{%\s*([^ }]+)\s*[^%]*\s*%}/; const regexComment = /^{#\s*[^#]+\s*#}/; let ticker = 1; return { startState () { return {inRaw: false}; }, token (stream, state) { let m; // This makes sure that adjacent tags still have unique types ticker *= -1; m = stream.match(regexTag, true); if (m) { const name = m[1]; if (state.inRaw && name === 'endraw') { state.inRaw = false; } else if (!state.inRaw && name === 'raw') { state.inRaw = true; } else if (state.inRaw) { // Inside raw tag so do nothing return null; } return `nunjucks-tag ${ticker}`; } if (!state.inRaw) { m = stream.match(regexVariable, true); if (m) { return `nunjucks-variable ${ticker}`; } } if (!state.inRaw) { m = stream.match(regexComment, true); if (m) { return `nunjucks-comment ${ticker}`; } } while (stream.next() != null) { if (stream.match(regexVariable, false)) break; if (stream.match(regexTag, false)) break; if (stream.match(regexComment, false)) break; } return null; } }; }