<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CodeMirror variables mode</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.61.1/codemirror.min.css"
integrity="sha512-xIf9AdJauwKIVtrVRZ0i4nHP61Ogx9fSRAkCLecmE2dL/U8ioWpDvFCAy4dcfecN72HHB9+7FfQj3aiO68aaaw=="
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
height: 100%;
}
</style>
</head>
<body>
<h1>CodeMirror variables mode</h1>
<p>
This is a <a href="https://codemirror.net/">CodeMirror</a> mode that brings
syntax highlighting for variables in strings.
</p>
<p>
<strong>MIME types defined:</strong>
<ul>
<li>text/x-variables</li>
</ul>
</p>
<p>
<strong>Options:</strong>
<ul>
<li>base_token: string: token returned outside variables; default: 'string'</li>
<li>escape_sequences: string, RegExp or null: possible escape sequences; default: '\\$'</li>
<li>escape_token: string: token returned when spotting escaped characters; default: 'string'</li>
<li>
variable_patterns: array of objects: possible variable patterns:
<ul>
<li>prefix: string or RegExp: prefix used to spot variables; mandatory; example: '${'</li>
<li>name: RegExp: pattern for acceptable variables names; mandatory; example: /\w+/</li>
<li>suffix: string or RegExp: suffix matching the prefix; optional; example: '}'</li>
<li>prefix_token: string: token returned for prefixes; optional; default: 'string strong'</li>
<li>name_token: string: token returned for names; optional; default: 'variable-2'</li>
<li>suffix_token: string: token returned for suffixes; optional; default: 'string strong'</li>
<li>error_token: string: token returned for errors; optional; default: 'error'</li>
</ul>
Patterns must be ordered based on their prefix, e.g. '${' before '$'.
</li>
</ul>
</p>
<h2 id="h2direct">Direct use</h2>
By default, codemirror-mode-variables supports $-prefixed variables with optional braces, e.g. $foo or ${bar}:
<textarea id="direct">$scheme://$host/somewhere/else/${tail}${is_args}${args}</textarea>
<h2 id="h2custom">Custom variable patterns</h2>
<p>Braces:</p>
<textarea id="custom0">{scheme}://{host}/somewhere/else/{tail}{is_args}{args}</textarea>
<p>Brackets:</p>
<textarea id="custom1">[scheme]://[host]/somewhere/else/[tail][is_args][args]</textarea>
<p>Apache-style:</p>
<textarea id="custom2">
abc$1def%2ghi${mapname:key|default}
abc%{SERVER_NAME}def%{ENV:variable}ghi%{SSL:SSL_CIPHER_USEKEYSIZE}
abc%{HTTP:Proxy-Connection}def%{LA-U:REMOTE_USER}ghi%{LA-F:REMOTE_USER}</textarea>
<p>printf-style:</p>
<textarea id="custom3">
abc%5dghi
abc%-5dghi
abc%05dghi
abc%+5dghi
abc%-+5dghi</textarea>
<h2 id="h2nested">Use as nested mode</h2>
<p>
Below is a demonstration of how the variables mode can be used to highlight variables within other languages (here, nginx).
</p>
<textarea id="nested">
server {
location ~ "(?x) # Enable PCRE extended mode
^
/user
/(?<action>login|logout|profile) # Also include 'profile' because...
/(?<tail>.*)
"
{
if ($tail ~ "^(some|[^/]+/really|compl(?:ex|icated)|stuff|t?here)$") {
return 307 "${scheme}://${host}/somewhere/else/${tail}${is_args}${args}";
}
# ...
}
}</textarea>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.61.1/codemirror.min.js"
integrity="sha512-ZTpbCvmiv7Zt4rK0ltotRJVRaSBKFQHQTrwfs6DoYlBYzO1MA6Oz2WguC+LkV8pGiHraYLEpo7Paa+hoVbCfKw=="
crossorigin="anonymous"
referrerpolicy="no-referrer">
</script>
<script src="src/variables.js"></script>
<script>
CodeMirror.defineMode('nginx-mini+variables', function(editor_options, mode_options) {
var variables_mode = CodeMirror.getMode(editor_options, {name: 'variables'});
return {
startState: function() {
return {
current: '',
variables_state: CodeMirror.startState(variables_mode),
expect_regex: false, // whether the next string should be a regex
};
},
copyState: function(os) { // os = original state
return {
current: os.current,
variables_state: CodeMirror.copyState(variables_mode, os.variables_state),
expect_regex: os.expect_regex,
};
},
token: function(stream, state) {
if (state.current === 'string' || state.current === 'regex') {
if (stream.eat('"')) {
state.current = '';
return 'string';
}
if (state.current !== 'regex') return variables_mode.token(stream, state.variables_state);
if (!stream.match('\\"')) stream.next();
return 'string';
}
var operator = stream.match(/(=|~|~\*|\^~)/);
if (operator) {
state.expect_regex = operator[1][0] === '~'; // ~ and ~* imply a regex
return 'operator';
}
if (stream.eat('"')) {
state.current = state.expect_regex ? 'regex' : 'string';
if (state.expect_regex) state.variables_state = CodeMirror.startState(variables_mode);
state.expect_regex = false; // ^ new regex, new PCRE state object
return 'string';
}
/* Minimalistic nginx syntax highlighting, just for demonstration purposes: */
if (stream.eat('#')) {
stream.skipToEnd();
return 'comment';
}
if (stream.match(/\b(?:if|return|server|location)\b/)) return 'keyword';
if (stream.match(/\$\w+/)) return 'variable-3';
if (stream.match(/\d+/)) return 'number';
stream.next();
return state.current;
},
};
});
</script>
<script>
var mode = { name: 'variables' };
var conf = {
lineNumbers: true,
indentWithTabs: true,
showCursorWhenSelecting: true,
mode: mode,
};
CodeMirror.fromTextArea(document.getElementById('direct'), conf);
mode.escape_sequences = '\\{';
mode.variable_patterns = [{'prefix': '{', 'suffix': '}', 'name': /\w+/ }];
CodeMirror.fromTextArea(document.getElementById('custom0'), conf);
mode.escape_sequences = '\\[';
mode.variable_patterns = [{'prefix': '[', 'suffix': ']', 'name': /\w+/ }];
CodeMirror.fromTextArea(document.getElementById('custom1'), conf);
// Based on https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html :
mode.escape_sequences = /\\[%$]/;
mode.variable_patterns = [];
mode.variable_patterns.push({prefix: '${', suffix: '}', name: /\w+:\w+(?:\|[^}]*)?/ });
mode.variable_patterns.push({prefix: '%{', suffix: '}', name: /(?:(?:ENV|SSL|HTTP|LA-[FU]):[\w-]+|[A-Z0-9_]+)/ });
mode.variable_patterns.push({prefix: /[$%]/, name: /\d/, prefix_token: 'variable-2 strong', name_token: 'variable-2 strong'});
CodeMirror.fromTextArea(document.getElementById('custom2'), conf);
// Based on https://alvinalexander.com/programming/printf-format-cheat-sheet/ :
mode.escape_sequences = /[\\%]%/;
mode.variable_patterns = [{prefix: '%', prefix_token: 'variable-2 strong', name_token: 'variable-2 strong', name: /-?\+?\d*[cdefiosux]/ }];
CodeMirror.fromTextArea(document.getElementById('custom3'), conf);
conf.mode = {name: 'nginx-mini+variables'};
CodeMirror.fromTextArea(document.getElementById('nested'), conf);
</script>
</body>
</html>