var hljs = new function() { /* Utility functions */ function escape(value) { return value.replace(/&/gm, '&').replace(//gm, '>'); } function tag(node) { return node.nodeName.toLowerCase(); } function testRe(re, lexeme) { var match = re && re.exec(lexeme); return match && match.index == 0; } function blockLanguage(block) { var classes = (block.className + ' ' + (block.parentNode ? block.parentNode.className : '')).split(/\s+/); classes = classes.map(function(c) {return c.replace(/^lang(uage)?-/, '');}); return classes.filter(function(c) {return getLanguage(c) || /no(-?)highlight/.test(c);})[0]; } function inherit(parent, obj) { var result = {}; for (var key in parent) result[key] = parent[key]; if (obj) for (var key in obj) result[key] = obj[key]; return result; }; /* Stream merging */ function nodeStream(node) { var result = []; (function _nodeStream(node, offset) { for (var child = node.firstChild; child; child = child.nextSibling) { if (child.nodeType == 3) offset += child.nodeValue.length; else if (child.nodeType == 1) { result.push({ event: 'start', offset: offset, node: child }); offset = _nodeStream(child, offset); // Prevent void elements from having an end tag that would actually // double them in the output. There are more void elements in HTML // but we list only those realistically expected in code display. if (!tag(child).match(/br|hr|img|input/)) { result.push({ event: 'stop', offset: offset, node: child }); } } } return offset; })(node, 0); return result; } function mergeStreams(original, highlighted, value) { var processed = 0; var result = ''; var nodeStack = []; function selectStream() { if (!original.length || !highlighted.length) { return original.length ? original : highlighted; } if (original[0].offset != highlighted[0].offset) { return (original[0].offset < highlighted[0].offset) ? original : highlighted; } /* To avoid starting the stream just before it should stop the order is ensured that original always starts first and closes last: if (event1 == 'start' && event2 == 'start') return original; if (event1 == 'start' && event2 == 'stop') return highlighted; if (event1 == 'stop' && event2 == 'start') return original; if (event1 == 'stop' && event2 == 'stop') return highlighted; ... which is collapsed to: */ return highlighted[0].event == 'start' ? original : highlighted; } function open(node) { function attr_str(a) {return ' ' + a.nodeName + '="' + escape(a.value) + '"';} result += '<' + tag(node) + Array.prototype.map.call(node.attributes, attr_str).join('') + '>'; } function close(node) { result += ''; } function render(event) { (event.event == 'start' ? open : close)(event.node); } while (original.length || highlighted.length) { var stream = selectStream(); result += escape(value.substr(processed, stream[0].offset - processed)); processed = stream[0].offset; if (stream == original) { /* On any opening or closing tag of the original markup we first close the entire highlighted node stack, then render the original tag along with all the following original tags at the same offset and then reopen all the tags on the highlighted stack. */ nodeStack.reverse().forEach(close); do { render(stream.splice(0, 1)[0]); stream = selectStream(); } while (stream == original && stream.length && stream[0].offset == processed); nodeStack.reverse().forEach(open); } else { if (stream[0].event == 'start') { nodeStack.push(stream[0].node); } else { nodeStack.pop(); } render(stream.splice(0, 1)[0]); } } return result + escape(value.substr(processed)); } /* Initialization */ function compileLanguage(language) { function reStr(re) { return (re && re.source) || re; } function langRe(value, global) { return RegExp( reStr(value), 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '') ); } function compileMode(mode, parent) { if (mode.compiled) return; mode.compiled = true; mode.keywords = mode.keywords || mode.beginKeywords; if (mode.keywords) { var compiled_keywords = {}; var flatten = function(className, str) { if (language.case_insensitive) { str = str.toLowerCase(); } str.split(' ').forEach(function(kw) { var pair = kw.split('|'); compiled_keywords[pair[0]] = [className, pair[1] ? Number(pair[1]) : 1]; }); }; if (typeof mode.keywords == 'string') { // string flatten('keyword', mode.keywords); } else { Object.keys(mode.keywords).forEach(function (className) { flatten(className, mode.keywords[className]); }); } mode.keywords = compiled_keywords; } mode.lexemesRe = langRe(mode.lexemes || /\b[A-Za-z0-9_]+\b/, true); if (parent) { if (mode.beginKeywords) { mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')\\b'; } if (!mode.begin) mode.begin = /\B|\b/; mode.beginRe = langRe(mode.begin); if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/; if (mode.end) mode.endRe = langRe(mode.end); mode.terminator_end = reStr(mode.end) || ''; if (mode.endsWithParent && parent.terminator_end) mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end; } if (mode.illegal) mode.illegalRe = langRe(mode.illegal); if (mode.relevance === undefined) mode.relevance = 1; if (!mode.contains) { mode.contains = []; } var expanded_contains = []; mode.contains.forEach(function(c) { if (c.variants) { c.variants.forEach(function(v) {expanded_contains.push(inherit(c, v));}); } else { expanded_contains.push(c == 'self' ? mode : c); } }); mode.contains = expanded_contains; mode.contains.forEach(function(c) {compileMode(c, mode);}); if (mode.starts) { compileMode(mode.starts, parent); } var terminators = mode.contains.map(function(c) { return c.beginKeywords ? '\\.?(' + c.begin + ')\\.?' : c.begin; }) .concat([mode.terminator_end, mode.illegal]) .map(reStr) .filter(Boolean); mode.terminators = terminators.length ? langRe(terminators.join('|'), true) : {exec: function(s) {return null;}}; } compileMode(language); } /* Core highlighting function. Accepts a language name, or an alias, and a string with the code to highlight. Returns an object with the following properties: - relevance (int) - value (an HTML string with highlighting markup) */ function highlight(name, value, ignore_illegals, continuation) { function subMode(lexeme, mode) { for (var i = 0; i < mode.contains.length; i++) { if (testRe(mode.contains[i].beginRe, lexeme)) { return mode.contains[i]; } } } function endOfMode(mode, lexeme) { if (testRe(mode.endRe, lexeme)) { return mode; } if (mode.endsWithParent) { return endOfMode(mode.parent, lexeme); } } function isIllegal(lexeme, mode) { return !ignore_illegals && testRe(mode.illegalRe, lexeme); } function keywordMatch(mode, match) { var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0]; return mode.keywords.hasOwnProperty(match_str) && mode.keywords[match_str]; } function buildSpan(classname, insideSpan, leaveOpen, noPrefix) { var classPrefix = noPrefix ? '' : options.classPrefix, openSpan = ''; return openSpan + insideSpan + closeSpan; } function processKeywords() { if (!top.keywords) return escape(mode_buffer); var result = ''; var last_index = 0; top.lexemesRe.lastIndex = 0; var match = top.lexemesRe.exec(mode_buffer); while (match) { result += escape(mode_buffer.substr(last_index, match.index - last_index)); var keyword_match = keywordMatch(top, match); if (keyword_match) { relevance += keyword_match[1]; result += buildSpan(keyword_match[0], escape(match[0])); } else { result += escape(match[0]); } last_index = top.lexemesRe.lastIndex; match = top.lexemesRe.exec(mode_buffer); } return result + escape(mode_buffer.substr(last_index)); } function processSubLanguage() { if (top.subLanguage && !languages[top.subLanguage]) { return escape(mode_buffer); } var result = top.subLanguage ? highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) : highlightAuto(mode_buffer); // Counting embedded language score towards the host language may be disabled // with zeroing the containing mode relevance. Usecase in point is Markdown that // allows XML everywhere and makes every XML snippet to have a much larger Markdown // score. if (top.relevance > 0) { relevance += result.relevance; } if (top.subLanguageMode == 'continuous') { continuations[top.subLanguage] = result.top; } return buildSpan(result.language, result.value, false, true); } function processBuffer() { return top.subLanguage !== undefined ? processSubLanguage() : processKeywords(); } function startNewMode(mode, lexeme) { var markup = mode.className? buildSpan(mode.className, '', true): ''; if (mode.returnBegin) { result += markup; mode_buffer = ''; } else if (mode.excludeBegin) { result += escape(lexeme) + markup; mode_buffer = ''; } else { result += markup; mode_buffer = lexeme; } top = Object.create(mode, {parent: {value: top}}); } function processLexeme(buffer, lexeme) { mode_buffer += buffer; if (lexeme === undefined) { result += processBuffer(); return 0; } var new_mode = subMode(lexeme, top); if (new_mode) { result += processBuffer(); startNewMode(new_mode, lexeme); return new_mode.returnBegin ? 0 : lexeme.length; } var end_mode = endOfMode(top, lexeme); if (end_mode) { var origin = top; if (!(origin.returnEnd || origin.excludeEnd)) { mode_buffer += lexeme; } result += processBuffer(); do { if (top.className) { result += ''; } relevance += top.relevance; top = top.parent; } while (top != end_mode.parent); if (origin.excludeEnd) { result += escape(lexeme); } mode_buffer = ''; if (end_mode.starts) { startNewMode(end_mode.starts, ''); } return origin.returnEnd ? 0 : lexeme.length; } if (isIllegal(lexeme, top)) throw new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '') + '"'); /* Parser should not reach this point as all types of lexemes should be caught earlier, but if it does due to some bug make sure it advances at least one character forward to prevent infinite looping. */ mode_buffer += lexeme; return lexeme.length || 1; } var language = getLanguage(name); if (!language) { throw new Error('Unknown language: "' + name + '"'); } compileLanguage(language); var top = continuation || language; var continuations = {}; // keep continuations for sub-languages var result = ''; for(var current = top; current != language; current = current.parent) { if (current.className) { result = buildSpan(current.className, '', true) + result; } } var mode_buffer = ''; var relevance = 0; try { var match, count, index = 0; while (true) { top.terminators.lastIndex = index; match = top.terminators.exec(value); if (!match) break; count = processLexeme(value.substr(index, match.index - index), match[0]); index = match.index + count; } processLexeme(value.substr(index)); for(var current = top; current.parent; current = current.parent) { // close dangling modes if (current.className) { result += ''; } }; return { relevance: relevance, value: result, language: name, top: top }; } catch (e) { if (e.message.indexOf('Illegal') != -1) { return { relevance: 0, value: escape(value) }; } else { throw e; } } } /* Highlighting with language detection. Accepts a string with the code to highlight. Returns an object with the following properties: - language (detected language) - relevance (int) - value (an HTML string with highlighting markup) - second_best (object with the same structure for second-best heuristically detected language, may be absent) */ function highlightAuto(text, languageSubset) { languageSubset = languageSubset || options.languages || Object.keys(languages); var result = { relevance: 0, value: escape(text) }; var second_best = result; languageSubset.forEach(function(name) { if (!getLanguage(name)) { return; } var current = highlight(name, text, false); current.language = name; if (current.relevance > second_best.relevance) { second_best = current; } if (current.relevance > result.relevance) { second_best = result; result = current; } }); if (second_best.language) { result.second_best = second_best; } return result; } /* Post-processing of the highlighted markup: - replace TABs with something more useful - replace real line-breaks with '
' for non-pre containers */ function fixMarkup(value) { if (options.tabReplace) { value = value.replace(/^((<[^>]+>|\t)+)/gm, function(match, p1, offset, s) { return p1.replace(/\t/g, options.tabReplace); }); } if (options.useBR) { value = value.replace(/\n/g, '
'); } return value; } function buildClassName(prevClassName, currentLang, resultLang) { var language = currentLang ? aliases[currentLang] : resultLang, result = [prevClassName.trim(), 'hljs']; if(language) { result.push(language); } return result.join(' ').trim(); } /* Applies highlighting to a DOM node containing code. Accepts a DOM node and two optional parameters for fixMarkup. */ function highlightBlock(block) { var language = blockLanguage(block); if (/no(-?)highlight/.test(language)) return; var node; if (options.useBR) { node = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); node.innerHTML = block.innerHTML.replace(/\n/g, '').replace(//g, '\n'); } else { node = block; } var text = node.textContent; var result = language ? highlight(language, text, true) : highlightAuto(text); var originalStream = nodeStream(node); if (originalStream.length) { var resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); resultNode.innerHTML = result.value; result.value = mergeStreams(originalStream, nodeStream(resultNode), text); } result.value = fixMarkup(result.value); block.innerHTML = result.value; block.className = buildClassName(block.className, language, result.language); block.result = { language: result.language, re: result.relevance }; if (result.second_best) { block.second_best = { language: result.second_best.language, re: result.second_best.relevance }; } } var options = { classPrefix: 'hljs-', tabReplace: null, useBR: false, languages: undefined }; /* Updates highlight.js global options with values passed in the form of an object */ function configure(user_options) { options = inherit(options, user_options); } /* Applies highlighting to all
..
blocks on a page. */ function initHighlighting() { if (initHighlighting.called) return; initHighlighting.called = true; var blocks = document.querySelectorAll('pre code'); Array.prototype.forEach.call(blocks, highlightBlock); } /* Attaches highlighting to the page load event. */ function initHighlightingOnLoad() { addEventListener('DOMContentLoaded', initHighlighting, false); addEventListener('load', initHighlighting, false); } var languages = {}; var aliases = {}; function registerLanguage(name, language) { var lang = languages[name] = language(this); if (lang.aliases) { lang.aliases.forEach(function(alias) {aliases[alias] = name;}); } } function listLanguages() { return Object.keys(languages); } function getLanguage(name) { return languages[name] || languages[aliases[name]]; } /* Interface definition */ this.highlight = highlight; this.highlightAuto = highlightAuto; this.fixMarkup = fixMarkup; this.highlightBlock = highlightBlock; this.configure = configure; this.initHighlighting = initHighlighting; this.initHighlightingOnLoad = initHighlightingOnLoad; this.registerLanguage = registerLanguage; this.listLanguages = listLanguages; this.getLanguage = getLanguage; this.inherit = inherit; // Common regexps this.IDENT_RE = '[a-zA-Z][a-zA-Z0-9_]*'; this.UNDERSCORE_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_]*'; this.NUMBER_RE = '\\b\\d+(\\.\\d+)?'; this.C_NUMBER_RE = '(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float this.BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b... this.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; // Common modes this.BACKSLASH_ESCAPE = { begin: '\\\\[\\s\\S]', relevance: 0 }; this.APOS_STRING_MODE = { className: 'string', begin: '\'', end: '\'', illegal: '\\n', contains: [this.BACKSLASH_ESCAPE] }; this.QUOTE_STRING_MODE = { className: 'string', begin: '"', end: '"', illegal: '\\n', contains: [this.BACKSLASH_ESCAPE] }; this.PHRASAL_WORDS_MODE = { begin: /\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/ }; this.C_LINE_COMMENT_MODE = { className: 'comment', begin: '//', end: '$', contains: [this.PHRASAL_WORDS_MODE] }; this.C_BLOCK_COMMENT_MODE = { className: 'comment', begin: '/\\*', end: '\\*/', contains: [this.PHRASAL_WORDS_MODE] }; this.HASH_COMMENT_MODE = { className: 'comment', begin: '#', end: '$', contains: [this.PHRASAL_WORDS_MODE] }; this.NUMBER_MODE = { className: 'number', begin: this.NUMBER_RE, relevance: 0 }; this.C_NUMBER_MODE = { className: 'number', begin: this.C_NUMBER_RE, relevance: 0 }; this.BINARY_NUMBER_MODE = { className: 'number', begin: this.BINARY_NUMBER_RE, relevance: 0 }; this.CSS_NUMBER_MODE = { className: 'number', begin: this.NUMBER_RE + '(' + '%|em|ex|ch|rem' + '|vw|vh|vmin|vmax' + '|cm|mm|in|pt|pc|px' + '|deg|grad|rad|turn' + '|s|ms' + '|Hz|kHz' + '|dpi|dpcm|dppx' + ')?', relevance: 0 }; this.REGEXP_MODE = { className: 'regexp', begin: /\//, end: /\/[gimuy]*/, illegal: /\n/, contains: [ this.BACKSLASH_ESCAPE, { begin: /\[/, end: /\]/, relevance: 0, contains: [this.BACKSLASH_ESCAPE] } ] }; this.TITLE_MODE = { className: 'title', begin: this.IDENT_RE, relevance: 0 }; this.UNDERSCORE_TITLE_MODE = { className: 'title', begin: this.UNDERSCORE_IDENT_RE, relevance: 0 }; }(); hljs.registerLanguage('gradle', function(hljs) { return { case_insensitive: true, keywords: { keyword: 'task project allprojects subprojects artifacts buildscript configurations ' + 'dependencies repositories sourceSets description delete from into include ' + 'exclude source classpath destinationDir includes options sourceCompatibility ' + 'targetCompatibility group flatDir doLast doFirst flatten todir fromdir ant ' + 'def abstract break case catch continue default do else extends final finally ' + 'for if implements instanceof native new private protected public return static ' + 'switch synchronized throw throws transient try volatile while strictfp package ' + 'import false null super this true antlrtask checkstyle codenarc copy boolean ' + 'byte char class double float int interface long short void compile runTime ' + 'file fileTree abs any append asList asWritable call collect compareTo count ' + 'div dump each eachByte eachFile eachLine every find findAll flatten getAt ' + 'getErr getIn getOut getText grep immutable inject inspect intersect invokeMethods ' + 'isCase join leftShift minus multiply newInputStream newOutputStream newPrintWriter ' + 'newReader newWriter next plus pop power previous print println push putAt read ' + 'readBytes readLines reverse reverseEach round size sort splitEachLine step subMap ' + 'times toInteger toList tokenize upto waitForOrKill withPrintWriter withReader ' + 'withStream withWriter withWriterAppend write writeLine' }, contains: [ hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, hljs.NUMBER_MODE, hljs.REGEXP_MODE ] } }); hljs.registerLanguage('nimrod', function(hljs) { return { keywords: { keyword: 'addr and as asm bind block break|0 case|0 cast const|0 continue|0 converter discard distinct|10 div do elif else|0 end|0 enum|0 except export finally for from generic if|0 import|0 in include|0 interface is isnot|10 iterator|10 let|0 macro method|10 mixin mod nil not notin|10 object|0 of or out proc|10 ptr raise ref|10 return shl shr static template|10 try|0 tuple type|0 using|0 var|0 when while|0 with without xor yield', literal: 'shared guarded stdin stdout stderr result|10 true false' }, contains: [ { className: 'decorator', // Actually pragma begin: /{\./, end: /\.}/, relevance: 10 }, { className: 'string', begin: /[a-zA-Z]\w*"/, end: /"/, contains: [{begin: /""/}] }, { className: 'string', begin: /([a-zA-Z]\w*)?"""/, end: /"""/ }, { className: 'string', begin: /"/, end: /"/, illegal: /\n/, contains: [{begin: /\\./}] }, { className: 'type', begin: /\b[A-Z]\w+\b/, relevance: 0 }, { className: 'type', begin: /\b(int|int8|int16|int32|int64|uint|uint8|uint16|uint32|uint64|float|float32|float64|bool|char|string|cstring|pointer|expr|stmt|void|auto|any|range|array|openarray|varargs|seq|set|clong|culong|cchar|cschar|cshort|cint|csize|clonglong|cfloat|cdouble|clongdouble|cuchar|cushort|cuint|culonglong|cstringarray|semistatic)\b/ }, { className: 'number', begin: /\b(0[xX][0-9a-fA-F][_0-9a-fA-F]*)('?[iIuU](8|16|32|64))?/, relevance: 0 }, { className: 'number', begin: /\b(0o[0-7][_0-7]*)('?[iIuUfF](8|16|32|64))?/, relevance: 0 }, { className: 'number', begin: /\b(0(b|B)[01][_01]*)('?[iIuUfF](8|16|32|64))?/, relevance: 0 }, { className: 'number', begin: /\b(\d[_\d]*)('?[iIuUfF](8|16|32|64))?/, relevance: 0 }, hljs.HASH_COMMENT_MODE ] } }); hljs.registerLanguage('vbnet', function(hljs) { return { aliases: ['vb'], case_insensitive: true, keywords: { keyword: 'addhandler addressof alias and andalso aggregate ansi as assembly auto binary by byref byval ' + /* a-b */ 'call case catch class compare const continue custom declare default delegate dim distinct do ' + /* c-d */ 'each equals else elseif end enum erase error event exit explicit finally for friend from function ' + /* e-f */ 'get global goto group handles if implements imports in inherits interface into is isfalse isnot istrue ' + /* g-i */ 'join key let lib like loop me mid mod module mustinherit mustoverride mybase myclass ' + /* j-m */ 'namespace narrowing new next not notinheritable notoverridable ' + /* n */ 'of off on operator option optional or order orelse overloads overridable overrides ' + /* o */ 'paramarray partial preserve private property protected public ' + /* p */ 'raiseevent readonly redim rem removehandler resume return ' + /* r */ 'select set shadows shared skip static step stop structure strict sub synclock ' + /* s */ 'take text then throw to try unicode until using when where while widening with withevents writeonly xor', /* t-x */ built_in: 'boolean byte cbool cbyte cchar cdate cdec cdbl char cint clng cobj csbyte cshort csng cstr ctype ' + /* b-c */ 'date decimal directcast double gettype getxmlnamespace iif integer long object ' + /* d-o */ 'sbyte short single string trycast typeof uinteger ulong ushort', /* s-u */ literal: 'true false nothing' }, illegal: '//|{|}|endif|gosub|variant|wend', /* reserved deprecated keywords */ contains: [ hljs.inherit(hljs.QUOTE_STRING_MODE, {contains: [{begin: '""'}]}), { className: 'comment', begin: '\'', end: '$', returnBegin: true, contains: [ { className: 'xmlDocTag', begin: '\'\'\'|' }, { className: 'xmlDocTag', begin: '' } ] }, hljs.C_NUMBER_MODE, { className: 'preprocessor', begin: '#', end: '$', keywords: 'if else elseif end region externalsource' } ] }; }); hljs.registerLanguage('rsl', function(hljs) { return { keywords: { keyword: 'float color point normal vector matrix while for if do return else break extern continue', built_in: 'abs acos ambient area asin atan atmosphere attribute calculatenormal ceil cellnoise ' + 'clamp comp concat cos degrees depth Deriv diffuse distance Du Dv environment exp ' + 'faceforward filterstep floor format fresnel incident length lightsource log match ' + 'max min mod noise normalize ntransform opposite option phong pnoise pow printf ' + 'ptlined radians random reflect refract renderinfo round setcomp setxcomp setycomp ' + 'setzcomp shadow sign sin smoothstep specular specularbrdf spline sqrt step tan ' + 'texture textureinfo trace transform vtransform xcomp ycomp zcomp' }, illegal: '"]', keywords: 'include', illegal: '\\n' }, hljs.C_LINE_COMMENT_MODE ] }, { className: 'stl_container', begin: '\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<', end: '>', keywords: CPP_KEYWORDS, contains: ['self'] }, { begin: hljs.IDENT_RE + '::' } ] }; }); hljs.registerLanguage('vhdl', function(hljs) { return { case_insensitive: true, keywords: { keyword: 'abs access after alias all and architecture array assert attribute begin block ' + 'body buffer bus case component configuration constant context cover disconnect ' + 'downto default else elsif end entity exit fairness file for force function generate ' + 'generic group guarded if impure in inertial inout is label library linkage literal ' + 'loop map mod nand new next nor not null of on open or others out package port ' + 'postponed procedure process property protected pure range record register reject ' + 'release rem report restrict restrict_guarantee return rol ror select sequence ' + 'severity shared signal sla sll sra srl strong subtype then to transport type ' + 'unaffected units until use variable vmode vprop vunit wait when while with xnor xor', typename: 'boolean bit character severity_level integer time delay_length natural positive ' + 'string bit_vector file_open_kind file_open_status std_ulogic std_ulogic_vector ' + 'std_logic std_logic_vector unsigned signed boolean_vector integer_vector ' + 'real_vector time_vector' }, illegal: '{', contains: [ hljs.C_BLOCK_COMMENT_MODE, // VHDL-2008 block commenting. { className: 'comment', begin: '--', end: '$' }, hljs.QUOTE_STRING_MODE, hljs.C_NUMBER_MODE, { className: 'literal', begin: '\'(U|X|0|1|Z|W|L|H|-)\'', contains: [hljs.BACKSLASH_ESCAPE] }, { className: 'attribute', begin: '\'[A-Za-z](_?[A-Za-z0-9])*', contains: [hljs.BACKSLASH_ESCAPE] } ] }; // return }); hljs.registerLanguage('vala', function(hljs) { return { keywords: { keyword: // Value types 'char uchar unichar int uint long ulong short ushort int8 int16 int32 int64 uint8 ' + 'uint16 uint32 uint64 float double bool struct enum string void ' + // Reference types 'weak unowned owned ' + // Modifiers 'async signal static abstract interface override ' + // Control Structures 'while do for foreach else switch case break default return try catch ' + // Visibility 'public private protected internal ' + // Other 'using new this get set const stdout stdin stderr var', built_in: 'DBus GLib CCode Gee Object', literal: 'false true null' }, contains: [ { className: 'class', beginKeywords: 'class interface delegate namespace', end: '{', excludeEnd: true, illegal: '[^,:\\n\\s\\.]', contains: [ hljs.UNDERSCORE_TITLE_MODE ] }, hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE, { className: 'string', begin: '"""', end: '"""', relevance: 5 }, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, hljs.C_NUMBER_MODE, { className: 'preprocessor', begin: '^#', end: '$', relevance: 2 }, { className: 'constant', begin: ' [A-Z_]+ ', relevance: 0 } ] }; }); hljs.registerLanguage('bash', function(hljs) { var VAR = { className: 'variable', variants: [ {begin: /\$[\w\d#@][\w\d_]*/}, {begin: /\$\{(.*?)\}/} ] }; var QUOTE_STRING = { className: 'string', begin: /"/, end: /"/, contains: [ hljs.BACKSLASH_ESCAPE, VAR, { className: 'variable', begin: /\$\(/, end: /\)/, contains: [hljs.BACKSLASH_ESCAPE] } ] }; var APOS_STRING = { className: 'string', begin: /'/, end: /'/ }; return { aliases: ['sh', 'zsh'], lexemes: /-?[a-z\.]+/, keywords: { keyword: 'if then else elif fi for break continue while in do done exit return set '+ 'declare case esac export exec', literal: 'true false', built_in: 'printf echo read cd pwd pushd popd dirs let eval unset typeset readonly '+ 'getopts source shopt caller type hash bind help sudo', operator: '-ne -eq -lt -gt -f -d -e -s -l -a' // relevance booster }, contains: [ { className: 'shebang', begin: /^#![^\n]+sh\s*$/, relevance: 10 }, { className: 'function', begin: /\w[\w\d_]*\s*\(\s*\)\s*\{/, returnBegin: true, contains: [hljs.inherit(hljs.TITLE_MODE, {begin: /\w[\w\d_]*/})], relevance: 0 }, hljs.HASH_COMMENT_MODE, hljs.NUMBER_MODE, QUOTE_STRING, APOS_STRING, VAR ] }; }); hljs.registerLanguage('fsharp', function(hljs) { var TYPEPARAM = { begin: '<', end: '>', contains: [ hljs.inherit(hljs.TITLE_MODE, {begin: /'[a-zA-Z0-9_]+/}) ] }; return { aliases: ['fs'], keywords: // monad builder keywords (at top, matches before non-bang kws) 'yield! return! let! do!' + // regular keywords 'abstract and as assert base begin class default delegate do done ' + 'downcast downto elif else end exception extern false finally for ' + 'fun function global if in inherit inline interface internal lazy let ' + 'match member module mutable namespace new null of open or ' + 'override private public rec return sig static struct then to ' + 'true try type upcast use val void when while with yield', contains: [ { className: 'string', begin: '@"', end: '"', contains: [{begin: '""'}] }, { className: 'string', begin: '"""', end: '"""' }, { className: 'comment', begin: '\\(\\*', end: '\\*\\)' }, { className: 'class', beginKeywords: 'type', end: '\\(|=|$', excludeEnd: true, contains: [ hljs.UNDERSCORE_TITLE_MODE, TYPEPARAM ] }, { className: 'annotation', begin: '\\[<', end: '>\\]', relevance: 10 }, { className: 'attribute', begin: '\\B(\'[A-Za-z])\\b', contains: [hljs.BACKSLASH_ESCAPE] }, hljs.C_LINE_COMMENT_MODE, hljs.inherit(hljs.QUOTE_STRING_MODE, {illegal: null}), hljs.C_NUMBER_MODE ] }; }); hljs.registerLanguage('smalltalk', function(hljs) { var VAR_IDENT_RE = '[a-z][a-zA-Z0-9_]*'; var CHAR = { className: 'char', begin: '\\$.{1}' }; var SYMBOL = { className: 'symbol', begin: '#' + hljs.UNDERSCORE_IDENT_RE }; return { aliases: ['st'], keywords: 'self super nil true false thisContext', // only 6 contains: [ { className: 'comment', begin: '"', end: '"' }, hljs.APOS_STRING_MODE, { className: 'class', begin: '\\b[A-Z][A-Za-z0-9_]*', relevance: 0 }, { className: 'method', begin: VAR_IDENT_RE + ':', relevance: 0 }, hljs.C_NUMBER_MODE, SYMBOL, CHAR, { className: 'localvars', // This looks more complicated than needed to avoid combinatorial // explosion under V8. It effectively means `| var1 var2 ... |` with // whitespace adjacent to `|` being optional. begin: '\\|[ ]*' + VAR_IDENT_RE + '([ ]+' + VAR_IDENT_RE + ')*[ ]*\\|', returnBegin: true, end: /\|/, illegal: /\S/, contains: [{begin: '(\\|[ ]*)?' + VAR_IDENT_RE}] }, { className: 'array', begin: '\\#\\(', end: '\\)', contains: [ hljs.APOS_STRING_MODE, CHAR, hljs.C_NUMBER_MODE, SYMBOL ] } ] }; }); hljs.registerLanguage('markdown', function(hljs) { return { aliases: ['md', 'mkdown', 'mkd'], contains: [ // highlight headers { className: 'header', variants: [ { begin: '^#{1,6}', end: '$' }, { begin: '^.+?\\n[=-]{2,}$' } ] }, // inline html { begin: '<', end: '>', subLanguage: 'xml', relevance: 0 }, // lists (indicators only) { className: 'bullet', begin: '^([*+-]|(\\d+\\.))\\s+' }, // strong segments { className: 'strong', begin: '[*_]{2}.+?[*_]{2}' }, // emphasis segments { className: 'emphasis', variants: [ { begin: '\\*.+?\\*' }, { begin: '_.+?_' , relevance: 0 } ] }, // blockquotes { className: 'blockquote', begin: '^>\\s+', end: '$' }, // code snippets { className: 'code', variants: [ { begin: '`.+?`' }, { begin: '^( {4}|\t)', end: '$' , relevance: 0 } ] }, // horizontal rules { className: 'horizontal_rule', begin: '^[-\\*]{3,}', end: '$' }, // using links - title and link { begin: '\\[.+?\\][\\(\\[].*?[\\)\\]]', returnBegin: true, contains: [ { className: 'link_label', begin: '\\[', end: '\\]', excludeBegin: true, returnEnd: true, relevance: 0 }, { className: 'link_url', begin: '\\]\\(', end: '\\)', excludeBegin: true, excludeEnd: true }, { className: 'link_reference', begin: '\\]\\[', end: '\\]', excludeBegin: true, excludeEnd: true } ], relevance: 10 }, { begin: '^\\[\.+\\]:', returnBegin: true, contains: [ { className: 'link_reference', begin: '\\[', end: '\\]:', excludeBegin: true, excludeEnd: true, starts: { className: 'link_url', end: '$' } } ] } ] }; }); hljs.registerLanguage('dart', function (hljs) { var SUBST = { className: 'subst', begin: '\\$\\{', end: '}', keywords: 'true false null this is new super' }; var STRING = { className: 'string', variants: [ { begin: 'r\'\'\'', end: '\'\'\'' }, { begin: 'r"""', end: '"""' }, { begin: 'r\'', end: '\'', illegal: '\\n' }, { begin: 'r"', end: '"', illegal: '\\n' }, { begin: '\'\'\'', end: '\'\'\'', contains: [hljs.BACKSLASH_ESCAPE, SUBST] }, { begin: '"""', end: '"""', contains: [hljs.BACKSLASH_ESCAPE, SUBST] }, { begin: '\'', end: '\'', illegal: '\\n', contains: [hljs.BACKSLASH_ESCAPE, SUBST] }, { begin: '"', end: '"', illegal: '\\n', contains: [hljs.BACKSLASH_ESCAPE, SUBST] } ] }; SUBST.contains = [ hljs.C_NUMBER_MODE, STRING ]; var KEYWORDS = { keyword: 'assert break case catch class const continue default do else enum extends false final finally for if ' + 'in is new null rethrow return super switch this throw true try var void while with', literal: 'abstract as dynamic export external factory get implements import library operator part set static typedef', built_in: // dart:core 'print Comparable DateTime Duration Function Iterable Iterator List Map Match Null Object Pattern RegExp Set ' + 'Stopwatch String StringBuffer StringSink Symbol Type Uri bool double int num ' + // dart:html 'document window querySelector querySelectorAll Element ElementList' }; return { keywords: KEYWORDS, contains: [ STRING, { className: 'dartdoc', begin: '/\\*\\*', end: '\\*/', subLanguage: 'markdown', subLanguageMode: 'continuous' }, { className: 'dartdoc', begin: '///', end: '$', subLanguage: 'markdown', subLanguageMode: 'continuous' }, hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE, { className: 'class', beginKeywords: 'class interface', end: '{', excludeEnd: true, contains: [ { beginKeywords: 'extends implements' }, hljs.UNDERSCORE_TITLE_MODE ] }, hljs.C_NUMBER_MODE, { className: 'annotation', begin: '@[A-Za-z]+' }, { begin: '=>' // No markup, just a relevance booster } ] } }); hljs.registerLanguage('erlang-repl', function(hljs) { return { keywords: { special_functions: 'spawn spawn_link self', reserved: 'after and andalso|10 band begin bnot bor bsl bsr bxor case catch cond div end fun if ' + 'let not of or orelse|10 query receive rem try when xor' }, contains: [ { className: 'prompt', begin: '^[0-9]+> ', relevance: 10 }, { className: 'comment', begin: '%', end: '$' }, { className: 'number', begin: '\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)', relevance: 0 }, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, { className: 'constant', begin: '\\?(::)?([A-Z]\\w*(::)?)+' }, { className: 'arrow', begin: '->' }, { className: 'ok', begin: 'ok' }, { className: 'exclamation_mark', begin: '!' }, { className: 'function_or_atom', begin: '(\\b[a-z\'][a-zA-Z0-9_\']*:[a-z\'][a-zA-Z0-9_\']*)|(\\b[a-z\'][a-zA-Z0-9_\']*)', relevance: 0 }, { className: 'variable', begin: '[A-Z][a-zA-Z0-9_\']*', relevance: 0 } ] }; }); hljs.registerLanguage('elixir', function(hljs) { var ELIXIR_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?'; var ELIXIR_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?'; var ELIXIR_KEYWORDS = 'and false then defined module in return redo retry end for true self when ' + 'next until do begin unless nil break not case cond alias while ensure or ' + 'include use alias fn quote'; var SUBST = { className: 'subst', begin: '#\\{', end: '}', lexemes: ELIXIR_IDENT_RE, keywords: ELIXIR_KEYWORDS }; var STRING = { className: 'string', contains: [hljs.BACKSLASH_ESCAPE, SUBST], variants: [ { begin: /'/, end: /'/ }, { begin: /"/, end: /"/ } ] }; var PARAMS = { endsWithParent: true, returnEnd: true, lexemes: ELIXIR_IDENT_RE, keywords: ELIXIR_KEYWORDS, relevance: 0 }; var FUNCTION = { className: 'function', beginKeywords: 'def defmacro', end: /\bdo\b/, contains: [ hljs.inherit(hljs.TITLE_MODE, { begin: ELIXIR_METHOD_RE, starts: PARAMS }) ] }; var CLASS = hljs.inherit(FUNCTION, { className: 'class', beginKeywords: 'defmodule defrecord', end: /\bdo\b|$|;/ }) var ELIXIR_DEFAULT_CONTAINS = [ STRING, hljs.HASH_COMMENT_MODE, CLASS, FUNCTION, { className: 'constant', begin: '(\\b[A-Z_]\\w*(.)?)+', relevance: 0 }, { className: 'symbol', begin: ':', contains: [STRING, {begin: ELIXIR_METHOD_RE}], relevance: 0 }, { className: 'symbol', begin: ELIXIR_IDENT_RE + ':', relevance: 0 }, { className: 'number', begin: '(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b', relevance: 0 }, { className: 'variable', begin: '(\\$\\W)|((\\$|\\@\\@?)(\\w+))' }, { begin: '->' }, { // regexp container begin: '(' + hljs.RE_STARTERS_RE + ')\\s*', contains: [ hljs.HASH_COMMENT_MODE, { className: 'regexp', illegal: '\\n', contains: [hljs.BACKSLASH_ESCAPE, SUBST], variants: [ { begin: '/', end: '/[a-z]*' }, { begin: '%r\\[', end: '\\][a-z]*' } ] } ], relevance: 0 } ]; SUBST.contains = ELIXIR_DEFAULT_CONTAINS; PARAMS.contains = ELIXIR_DEFAULT_CONTAINS; return { lexemes: ELIXIR_IDENT_RE, keywords: ELIXIR_KEYWORDS, contains: ELIXIR_DEFAULT_CONTAINS }; }); hljs.registerLanguage('diff', function(hljs) { return { aliases: ['patch'], contains: [ { className: 'chunk', relevance: 10, variants: [ {begin: /^\@\@ +\-\d+,\d+ +\+\d+,\d+ +\@\@$/}, {begin: /^\*\*\* +\d+,\d+ +\*\*\*\*$/}, {begin: /^\-\-\- +\d+,\d+ +\-\-\-\-$/} ] }, { className: 'header', variants: [ {begin: /Index: /, end: /$/}, {begin: /=====/, end: /=====$/}, {begin: /^\-\-\-/, end: /$/}, {begin: /^\*{3} /, end: /$/}, {begin: /^\+\+\+/, end: /$/}, {begin: /\*{5}/, end: /\*{5}$/} ] }, { className: 'addition', begin: '^\\+', end: '$' }, { className: 'deletion', begin: '^\\-', end: '$' }, { className: 'change', begin: '^\\!', end: '$' } ] }; }); hljs.registerLanguage('avrasm', function(hljs) { return { case_insensitive: true, lexemes: '\\.?' + hljs.IDENT_RE, keywords: { keyword: /* mnemonic */ 'adc add adiw and andi asr bclr bld brbc brbs brcc brcs break breq brge brhc brhs ' + 'brid brie brlo brlt brmi brne brpl brsh brtc brts brvc brvs bset bst call cbi cbr ' + 'clc clh cli cln clr cls clt clv clz com cp cpc cpi cpse dec eicall eijmp elpm eor ' + 'fmul fmuls fmulsu icall ijmp in inc jmp ld ldd ldi lds lpm lsl lsr mov movw mul ' + 'muls mulsu neg nop or ori out pop push rcall ret reti rjmp rol ror sbc sbr sbrc sbrs ' + 'sec seh sbi sbci sbic sbis sbiw sei sen ser ses set sev sez sleep spm st std sts sub ' + 'subi swap tst wdr', built_in: /* general purpose registers */ 'r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 ' + 'r23 r24 r25 r26 r27 r28 r29 r30 r31 x|0 xh xl y|0 yh yl z|0 zh zl ' + /* IO Registers (ATMega128) */ 'ucsr1c udr1 ucsr1a ucsr1b ubrr1l ubrr1h ucsr0c ubrr0h tccr3c tccr3a tccr3b tcnt3h ' + 'tcnt3l ocr3ah ocr3al ocr3bh ocr3bl ocr3ch ocr3cl icr3h icr3l etimsk etifr tccr1c ' + 'ocr1ch ocr1cl twcr twdr twar twsr twbr osccal xmcra xmcrb eicra spmcsr spmcr portg ' + 'ddrg ping portf ddrf sreg sph spl xdiv rampz eicrb eimsk gimsk gicr eifr gifr timsk ' + 'tifr mcucr mcucsr tccr0 tcnt0 ocr0 assr tccr1a tccr1b tcnt1h tcnt1l ocr1ah ocr1al ' + 'ocr1bh ocr1bl icr1h icr1l tccr2 tcnt2 ocr2 ocdr wdtcr sfior eearh eearl eedr eecr ' + 'porta ddra pina portb ddrb pinb portc ddrc pinc portd ddrd pind spdr spsr spcr udr0 ' + 'ucsr0a ucsr0b ubrr0l acsr admux adcsr adch adcl porte ddre pine pinf', preprocessor: '.byte .cseg .db .def .device .dseg .dw .endmacro .equ .eseg .exit .include .list ' + '.listmac .macro .nolist .org .set' }, contains: [ hljs.C_BLOCK_COMMENT_MODE, {className: 'comment', begin: ';', end: '$', relevance: 0}, hljs.C_NUMBER_MODE, // 0x..., decimal, float hljs.BINARY_NUMBER_MODE, // 0b... { className: 'number', begin: '\\b(\\$[a-zA-Z0-9]+|0o[0-7]+)' // $..., 0o... }, hljs.QUOTE_STRING_MODE, { className: 'string', begin: '\'', end: '[^\\\\]\'', illegal: '[^\\\\][^\']' }, {className: 'label', begin: '^[A-Za-z0-9_.$]+:'}, {className: 'preprocessor', begin: '#', end: '$'}, { // подстановка в «.macro» className: 'localvars', begin: '@[0-9]+' } ] }; }); hljs.registerLanguage('json', function(hljs) { var LITERALS = {literal: 'true false null'}; var TYPES = [ hljs.QUOTE_STRING_MODE, hljs.C_NUMBER_MODE ]; var VALUE_CONTAINER = { className: 'value', end: ',', endsWithParent: true, excludeEnd: true, contains: TYPES, keywords: LITERALS }; var OBJECT = { begin: '{', end: '}', contains: [ { className: 'attribute', begin: '\\s*"', end: '"\\s*:\\s*', excludeBegin: true, excludeEnd: true, contains: [hljs.BACKSLASH_ESCAPE], illegal: '\\n', starts: VALUE_CONTAINER } ], illegal: '\\S' }; var ARRAY = { begin: '\\[', end: '\\]', contains: [hljs.inherit(VALUE_CONTAINER, {className: null})], // inherit is also a workaround for a bug that makes shared modes with endsWithParent compile only the ending of one of the parents illegal: '\\S' }; TYPES.splice(TYPES.length, 0, OBJECT, ARRAY); return { contains: TYPES, keywords: LITERALS, illegal: '\\S' }; }); hljs.registerLanguage('matlab', function(hljs) { var COMMON_CONTAINS = [ hljs.C_NUMBER_MODE, { className: 'string', begin: '\'', end: '\'', contains: [hljs.BACKSLASH_ESCAPE, {begin: '\'\''}] } ]; var TRANSPOSE = { relevance: 0, contains: [ { className: 'operator', begin: /'['\.]*/ } ] }; return { keywords: { keyword: 'break case catch classdef continue else elseif end enumerated events for function ' + 'global if methods otherwise parfor persistent properties return spmd switch try while', built_in: 'sin sind sinh asin asind asinh cos cosd cosh acos acosd acosh tan tand tanh atan ' + 'atand atan2 atanh sec secd sech asec asecd asech csc cscd csch acsc acscd acsch cot ' + 'cotd coth acot acotd acoth hypot exp expm1 log log1p log10 log2 pow2 realpow reallog ' + 'realsqrt sqrt nthroot nextpow2 abs angle complex conj imag real unwrap isreal ' + 'cplxpair fix floor ceil round mod rem sign airy besselj bessely besselh besseli ' + 'besselk beta betainc betaln ellipj ellipke erf erfc erfcx erfinv expint gamma ' + 'gammainc gammaln psi legendre cross dot factor isprime primes gcd lcm rat rats perms ' + 'nchoosek factorial cart2sph cart2pol pol2cart sph2cart hsv2rgb rgb2hsv zeros ones ' + 'eye repmat rand randn linspace logspace freqspace meshgrid accumarray size length ' + 'ndims numel disp isempty isequal isequalwithequalnans cat reshape diag blkdiag tril ' + 'triu fliplr flipud flipdim rot90 find sub2ind ind2sub bsxfun ndgrid permute ipermute ' + 'shiftdim circshift squeeze isscalar isvector ans eps realmax realmin pi i inf nan ' + 'isnan isinf isfinite j why compan gallery hadamard hankel hilb invhilb magic pascal ' + 'rosser toeplitz vander wilkinson' }, illegal: '(//|"|#|/\\*|\\s+/\\w+)', contains: [ { className: 'function', beginKeywords: 'function', end: '$', contains: [ hljs.UNDERSCORE_TITLE_MODE, { className: 'params', begin: '\\(', end: '\\)' }, { className: 'params', begin: '\\[', end: '\\]' } ] }, { begin: /[a-zA-Z_][a-zA-Z_0-9]*'['\.]*/, returnBegin: true, relevance: 0, contains: [ {begin: /[a-zA-Z_][a-zA-Z_0-9]*/, relevance: 0}, TRANSPOSE.contains[0] ] }, { className: 'matrix', begin: '\\[', end: '\\]', contains: COMMON_CONTAINS, relevance: 0, starts: TRANSPOSE }, { className: 'cell', begin: '\\{', end: /\}/, contains: COMMON_CONTAINS, relevance: 0, illegal: /:/, starts: TRANSPOSE }, { // transpose operators at the end of a function call begin: /\)/, relevance: 0, starts: TRANSPOSE }, { className: 'comment', begin: '\\%', end: '$' } ].concat(COMMON_CONTAINS) }; }); hljs.registerLanguage('xml', function(hljs) { var XML_IDENT_RE = '[A-Za-z0-9\\._:-]+'; var PHP = { begin: /<\?(php)?(?!\w)/, end: /\?>/, subLanguage: 'php', subLanguageMode: 'continuous' }; var TAG_INTERNALS = { endsWithParent: true, illegal: /]+/} ] } ] } ] }; return { aliases: ['html', 'xhtml', 'rss', 'atom', 'xsl', 'plist'], case_insensitive: true, contains: [ { className: 'doctype', begin: '', relevance: 10, contains: [{begin: '\\[', end: '\\]'}] }, { className: 'comment', begin: '', relevance: 10 }, { className: 'cdata', begin: '<\\!\\[CDATA\\[', end: '\\]\\]>', relevance: 10 }, { className: 'tag', /* The lookahead pattern (?=...) ensures that 'begin' only matches '|$)', end: '>', keywords: {title: 'style'}, contains: [TAG_INTERNALS], starts: { end: '', returnEnd: true, subLanguage: 'css' } }, { className: 'tag', // See the comment in the