diff --git a/src/languages/actionscript.js b/src/languages/actionscript.js index 77c9e81f9c..6e99a5cbc4 100644 --- a/src/languages/actionscript.js +++ b/src/languages/actionscript.js @@ -1,37 +1,30 @@ -import { insertBefore } from '../util/language-util.js'; import javascript from './javascript.js'; /** @type {import('../types.d.ts').LanguageProto<'actionscript'>} */ export default { id: 'actionscript', base: javascript, - grammar ({ base }) { - const className = /** @type {GrammarToken} */ (base['class-name']); - className.alias = 'function'; - - delete base['doc-comment']; - - // doesn't work with AS because AS is too complex - delete base['parameter']; - delete base['literal-property']; - - insertBefore(base, 'string', { - 'xml': { - pattern: - /(^|[^.])<\/?\w+(?:\s+[^\s>\/=]+=("|')(?:\\[\s\S]|(?!\2)[^\\])*\2)*\s*\/?>/, - lookbehind: true, - inside: 'markup', - }, - }); - + grammar () { return { 'keyword': /\b(?:as|break|case|catch|class|const|default|delete|do|dynamic|each|else|extends|final|finally|for|function|get|if|implements|import|in|include|instanceof|interface|internal|is|namespace|native|new|null|override|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|use|var|void|while|with)\b/, 'operator': /\+\+|--|(?:[+\-*\/%^]|&&?|\|\|?|<>?>?|[!=]=?)=?|[~?@]/, + // doesn't work with AS because AS is too complex + $delete: ['doc-comment', 'parameter', 'literal-property'], + $merge: { + 'class-name': { + alias: 'function', + }, + }, + $insert: { + 'xml': { + $before: 'string', + pattern: + /(^|[^.])<\/?\w+(?:\s+[^\s>\/=]+=("|')(?:\\[\s\S]|(?!\2)[^\\])*\2)*\s*\/?>/, + lookbehind: true, + inside: 'markup', + }, + }, }; }, }; - -/** - * @typedef {import('../types.d.ts').GrammarToken} GrammarToken - */ diff --git a/src/languages/aspnet.js b/src/languages/aspnet.js index 185e7d05c4..931eed8224 100644 --- a/src/languages/aspnet.js +++ b/src/languages/aspnet.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import csharp from './csharp.js'; import markup from './markup.js'; @@ -16,42 +15,11 @@ export default { pattern: /<%\s*?[$=%#:]{0,2}|%>/, alias: 'tag', }, - $rest: /** @type {Grammar['$rest']} */ ('csharp'), + $rest: 'csharp', }, }); - const tag = - /** @type {GrammarToken & { inside: { 'attr-value': { inside: Grammar } } }} */ ( - base['tag'] - ); - - // Regexp copied from markup, with a negative look-ahead added - tag.pattern = - /<(?!%)\/?[^\s>\/]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/; - - // match directives of attribute value foo="<% Bar %>" - insertBefore(tag.inside['attr-value'].inside, 'punctuation', { - 'directive': directive, - }); - - insertBefore(base, 'comment', { - 'asp-comment': { - pattern: /<%--[\s\S]*?--%>/, - alias: ['asp', 'comment'], - }, - }); - - // script runat="server" contains csharp, not javascript - insertBefore(base, 'script' in base ? 'script' : 'tag', { - 'asp-script': { - pattern: /(]*>)[\s\S]*?(?=<\/script>)/i, - lookbehind: true, - alias: ['asp', 'script'], - inside: 'csharp', - }, - }); - - return /** @type {Grammar} */ ({ + return { 'page-directive': { pattern: /<%\s*@.*%>/, alias: 'tag', @@ -61,15 +29,42 @@ export default { /<%\s*@\s*(?:Assembly|Control|Implements|Import|Master(?:Type)?|OutputCache|Page|PreviousPageType|Reference|Register)?|%>/i, alias: 'tag', }, - $rest: /** @type {Grammar['$rest']} */ (tag.inside), + $rest: /** @type {GrammarToken} */ (base['tag']).inside, }, }, 'directive': directive, - }); + $merge: { + 'tag': { + // Regexp copied from markup, with a negative look-ahead added + pattern: + /<(?!%)\/?[^\s>\/]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/, + }, + }, + $insertBefore: { + 'tag/attr-value/punctuation': { + // match directives of attribute value foo="<% Bar %>" + 'directive': directive, + }, + 'comment': { + 'asp-comment': { + pattern: /<%--[\s\S]*?--%>/, + alias: ['asp', 'comment'], + }, + }, + // script runat="server" contains csharp, not javascript + ['script' in base ? 'script' : 'tag']: { + 'asp-script': { + pattern: /(]*>)[\s\S]*?(?=<\/script>)/i, + lookbehind: true, + alias: ['asp', 'script'], + inside: 'csharp', + }, + }, + }, + }; }, }; /** - * @typedef {import('../types.d.ts').Grammar} Grammar * @typedef {import('../types.d.ts').GrammarToken} GrammarToken */ diff --git a/src/languages/birb.js b/src/languages/birb.js index 1e9cf2a663..7398425084 100644 --- a/src/languages/birb.js +++ b/src/languages/birb.js @@ -1,19 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'birb'>} */ export default { id: 'birb', base: clike, - grammar ({ base }) { - insertBefore(base, 'function', { - 'metadata': { - pattern: /<\w+>/, - greedy: true, - alias: 'symbol', - }, - }); - + grammar () { return { 'string': { pattern: /r?("|')(?:\\.|(?!\1)[^\\])*\1/, @@ -29,6 +20,14 @@ export default { /\b(?:assert|break|case|class|const|default|else|enum|final|follows|for|grab|if|nest|new|next|noSeeb|return|static|switch|throw|var|void|while)\b/, 'operator': /\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?|:/, 'variable': /\b[a-z_]\w*\b/, + $insert: { + 'metadata': { + $before: 'function', + pattern: /<\w+>/, + greedy: true, + alias: 'symbol', + }, + }, }; }, }; diff --git a/src/languages/bison.js b/src/languages/bison.js index e4f44750f2..fce19488fb 100644 --- a/src/languages/bison.js +++ b/src/languages/bison.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import c from './c.js'; /** @type {import('../types.d.ts').LanguageProto<'bison'>} */ @@ -6,48 +5,45 @@ export default { id: 'bison', base: c, grammar ({ base }) { - insertBefore(base, 'comment', { - 'bison': { - // This should match all the beginning of the file - // including the prologue(s), the bison declarations and - // the grammar rules. - pattern: /^(?:[^%]|%(?!%))*%%[\s\S]*?%%/, - inside: { - 'c': { - // Allow for one level of nested braces - pattern: /%\{[\s\S]*?%\}|\{(?:\{[^}]*\}|[^{}])*\}/, - inside: /** @type {Grammar} */ ({ - 'delimiter': { - pattern: /^%?\{|%?\}$/, - alias: 'punctuation', - }, - 'bison-variable': { - pattern: /[$@](?:<[^\s>]+>)?[\w$]+/, - alias: 'variable', - inside: { - 'punctuation': /<|>/, + return { + $insert: { + 'bison': { + $before: 'comment', + // This should match all the beginning of the file + // including the prologue(s), the bison declarations and + // the grammar rules. + pattern: /^(?:[^%]|%(?!%))*%%[\s\S]*?%%/, + inside: { + 'c': { + // Allow for one level of nested braces + pattern: /%\{[\s\S]*?%\}|\{(?:\{[^}]*\}|[^{}])*\}/, + inside: { + 'delimiter': { + pattern: /^%?\{|%?\}$/, + alias: 'punctuation', + }, + 'bison-variable': { + pattern: /[$@](?:<[^\s>]+>)?[\w$]+/, + alias: 'variable', + inside: { + 'punctuation': /<|>/, + }, }, + $rest: 'c', }, - $rest: /** @type {Grammar['$rest']} */ ('c'), - }), - }, - 'comment': base.comment, - 'string': base.string, - 'property': /\S+(?=:)/, - 'keyword': /%\w+/, - 'number': { - pattern: /(^|[^@])\b(?:0x[\da-f]+|\d+)/i, - lookbehind: true, + }, + 'comment': base.comment, + 'string': base.string, + 'property': /\S+(?=:)/, + 'keyword': /%\w+/, + 'number': { + pattern: /(^|[^@])\b(?:0x[\da-f]+|\d+)/i, + lookbehind: true, + }, + 'punctuation': /%[%?]|[|:;\[\]<>]/, }, - 'punctuation': /%[%?]|[|:;\[\]<>]/, }, }, - }); - - return {}; + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/c.js b/src/languages/c.js index 9b96dcdebb..8081c35e00 100644 --- a/src/languages/c.js +++ b/src/languages/c.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'c'>} */ @@ -7,75 +6,8 @@ export default { base: clike, optional: 'opencl-extensions', grammar ({ base, getOptionalLanguage }) { - insertBefore(base, 'string', { - 'char': { - // https://en.cppreference.com/w/c/language/character_constant - pattern: /'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/, - greedy: true, - }, - }); - - insertBefore(base, 'string', { - 'macro': { - // allow for multiline macro definitions - // spaces after the # character compile fine with gcc - pattern: - /(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im, - lookbehind: true, - greedy: true, - alias: 'property', - inside: { - 'string': [ - { - // highlight the path of the include statement as a string - pattern: /^(#\s*include\s*)<[^>]+>/, - lookbehind: true, - }, - /** @type {GrammarToken} */ (base['string']), - ], - 'char': base['char'], - 'comment': base['comment'], - 'macro-name': [ - { - pattern: /(^#\s*define\s+)\w+\b(?!\()/i, - lookbehind: true, - }, - { - pattern: /(^#\s*define\s+)\w+\b(?=\()/i, - lookbehind: true, - alias: 'function', - }, - ], - // highlight macro directives as keywords - 'directive': { - pattern: /^(#\s*)[a-z]+/, - lookbehind: true, - alias: 'keyword', - }, - 'directive-hash': /^#/, - 'punctuation': /##|\\(?=[\r\n])/, - 'expression': { - pattern: /\S[\s\S]*/, - inside: 'c', - }, - }, - }, - }); - - insertBefore(base, 'function', { - // highlight predefined macros as constants - 'constant': - /\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/, - }); - - delete base['boolean']; - /* OpenCL host API */ const extensions = getOptionalLanguage('opencl-extensions'); - if (extensions) { - insertBefore(base, 'keyword', /** @type {GrammarTokens} */ (extensions)); - delete base['type-opencl-host-cpp']; - } return { 'comment': { @@ -98,6 +30,69 @@ export default { 'number': /(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i, 'operator': />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/, + $insert: { + 'char': { + $before: 'string', + // https://en.cppreference.com/w/c/language/character_constant + pattern: /'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/, + greedy: true, + }, + 'macro': { + $before: 'string', + // allow for multiline macro definitions + // spaces after the # character compile fine with gcc + pattern: + /(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im, + lookbehind: true, + greedy: true, + alias: 'property', + inside: { + 'string': [ + { + // highlight the path of the include statement as a string + pattern: /^(#\s*include\s*)<[^>]+>/, + lookbehind: true, + }, + /** @type {GrammarToken} */ (base['string']), + ], + 'char': base['char'], + 'comment': base['comment'], + 'macro-name': [ + { + pattern: /(^#\s*define\s+)\w+\b(?!\()/i, + lookbehind: true, + }, + { + pattern: /(^#\s*define\s+)\w+\b(?=\()/i, + lookbehind: true, + alias: 'function', + }, + ], + // highlight macro directives as keywords + 'directive': { + pattern: /^(#\s*)[a-z]+/, + lookbehind: true, + alias: 'keyword', + }, + 'directive-hash': /^#/, + 'punctuation': /##|\\(?=[\r\n])/, + 'expression': { + pattern: /\S[\s\S]*/, + inside: 'c', + }, + }, + }, + // highlight predefined macros as constants + 'constant': { + $before: 'function', + pattern: + /\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/, + }, + }, + $insertBefore: { + 'function': /** @type {import('../types.d.ts').GrammarTokens} */ (extensions), + }, + $delete: ['boolean', ...(extensions ? ['type-opencl-host-cpp'] : [])], }; }, }; diff --git a/src/languages/cfscript.js b/src/languages/cfscript.js index 67be0b0775..6ea2f68bd1 100644 --- a/src/languages/cfscript.js +++ b/src/languages/cfscript.js @@ -1,14 +1,13 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'cfscript'>} */ export default { id: 'cfscript', - require: clike, + base: clike, alias: 'cfc', - grammar ({ extend }) { + grammar () { // https://cfdocs.org/script - const cfscript = extend('clike', { + return { 'comment': [ { pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/, @@ -42,19 +41,16 @@ export default { /\b(?:any|array|binary|boolean|date|guid|numeric|query|string|struct|uuid|void|xml)\b/, alias: 'builtin', }, - }); - - insertBefore(cfscript, 'keyword', { - // This must be declared before keyword because we use "function" inside the lookahead - 'function-variable': { - pattern: - /[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/, - alias: 'function', + $insert: { + // This must be declared before keyword because we use "function" inside the lookahead + 'function-variable': { + $before: 'keyword', + pattern: + /[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/, + alias: 'function', + }, }, - }); - - delete cfscript['class-name']; - - return cfscript; + $delete: ['class-name'], + }; }, }; diff --git a/src/languages/chaiscript.js b/src/languages/chaiscript.js index 12f031101b..ea439a8631 100644 --- a/src/languages/chaiscript.js +++ b/src/languages/chaiscript.js @@ -1,5 +1,4 @@ import { toArray } from '../util/iterables.js'; -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; import cpp from './cpp.js'; @@ -8,44 +7,7 @@ export default { id: 'chaiscript', base: clike, require: cpp, - grammar ({ base, languages }) { - insertBefore(base, 'operator', { - 'parameter-type': { - // e.g. def foo(int x, Vector y) {...} - pattern: /([,(]\s*)\w+(?=\s+\w)/, - lookbehind: true, - alias: 'class-name', - }, - }); - - insertBefore(base, 'string', { - 'string-interpolation': { - pattern: - /(^|[^\\])"(?:[^"$\\]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*"/, - lookbehind: true, - greedy: true, - inside: { - 'interpolation': { - pattern: - /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}/, - lookbehind: true, - inside: { - 'interpolation-expression': { - pattern: /(^\$\{)[\s\S]+(?=\}$)/, - lookbehind: true, - inside: 'chaiscript', - }, - 'interpolation-punctuation': { - pattern: /^\$\{|\}$/, - alias: 'punctuation', - }, - }, - }, - 'string': /[\s\S]+/, - }, - }, - }); - + grammar ({ languages }) { return { 'string': { pattern: /(^|[^\\])'(?:[^'\\]|\\[\s\S])*'/, @@ -66,8 +28,48 @@ export default { ], 'keyword': /\b(?:attr|auto|break|case|catch|class|continue|def|default|else|finally|for|fun|global|if|return|switch|this|try|var|while)\b/, - 'number': [...toArray(languages.cpp.number), /\b(?:Infinity|NaN)\b/], + 'number': [ + ...toArray( + /** @type {import('../types.d.ts').GrammarTokens} */ (languages.cpp).number + ), + /\b(?:Infinity|NaN)\b/, + ], 'operator': />>=?|<<=?|\|\||&&|:[:=]?|--|\+\+|[=!<>+\-*/%|&^]=?|[?~]|`[^`\r\n]{1,4}`/, + $insert: { + 'parameter-type': { + $before: 'operator', + // e.g. def foo(int x, Vector y) {...} + pattern: /([,(]\s*)\w+(?=\s+\w)/, + lookbehind: true, + alias: 'class-name', + }, + 'string-interpolation': { + $before: 'string', + pattern: + /(^|[^\\])"(?:[^"$\\]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*"/, + lookbehind: true, + greedy: true, + inside: { + 'interpolation': { + pattern: + /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}/, + lookbehind: true, + inside: { + 'interpolation-expression': { + pattern: /(^\$\{)[\s\S]+(?=\}$)/, + lookbehind: true, + inside: 'chaiscript', + }, + 'interpolation-punctuation': { + pattern: /^\$\{|\}$/, + alias: 'punctuation', + }, + }, + }, + 'string': /[\s\S]+/, + }, + }, + }, }; }, }; diff --git a/src/languages/cilkc.js b/src/languages/cilkc.js index 88ca73fb4f..aee444364b 100644 --- a/src/languages/cilkc.js +++ b/src/languages/cilkc.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import c from './c.js'; /** @type {import('../types.d.ts').LanguageProto<'cilkc'>} */ @@ -6,13 +5,15 @@ export default { id: 'cilkc', base: c, alias: 'cilk-c', - grammar ({ base }) { - insertBefore(base, 'function', { - 'parallel-keyword': { - pattern: /\bcilk_(?:for|reducer|s(?:cope|pawn|ync))\b/, - alias: 'keyword', + grammar () { + return { + $insert: { + 'parallel-keyword': { + $before: 'function', + pattern: /\bcilk_(?:for|reducer|s(?:cope|pawn|ync))\b/, + alias: 'keyword', + }, }, - }); - return {}; + }; }, }; diff --git a/src/languages/cilkcpp.js b/src/languages/cilkcpp.js index 9e34309b84..9168bbfe35 100644 --- a/src/languages/cilkcpp.js +++ b/src/languages/cilkcpp.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import cpp from './cpp.js'; /** @type {import('../types.d.ts').LanguageProto<'cilkcpp'>} */ @@ -6,13 +5,15 @@ export default { id: 'cilkcpp', base: cpp, alias: ['cilk-cpp', 'cilk'], - grammar ({ base }) { - insertBefore(base, 'function', { - 'parallel-keyword': { - pattern: /\bcilk_(?:for|reducer|s(?:cope|pawn|ync))\b/, - alias: 'keyword', + grammar () { + return { + $insert: { + 'parallel-keyword': { + $before: 'function', + pattern: /\bcilk_(?:for|reducer|s(?:cope|pawn|ync))\b/, + alias: 'keyword', + }, }, - }); - return {}; + }; }, }; diff --git a/src/languages/coffeescript.js b/src/languages/coffeescript.js index 8f3dea62c3..2d625b7825 100644 --- a/src/languages/coffeescript.js +++ b/src/languages/coffeescript.js @@ -1,12 +1,11 @@ -import { insertBefore } from '../util/language-util.js'; import javascript from './javascript.js'; /** @type {import('../types.d.ts').LanguageProto<'coffeescript'>} */ export default { id: 'coffeescript', - require: javascript, + base: javascript, alias: 'coffee', - grammar ({ extend }) { + grammar () { // Ignore comments starting with { to privilege string interpolation highlighting const comment = /#(?!\{).+/; const interpolation = { @@ -14,7 +13,7 @@ export default { alias: 'variable', }; - const coffeescript = extend('javascript', { + return { 'comment': comment, 'string': [ // Strings are multiline @@ -38,67 +37,62 @@ export default { pattern: /@(?!\d)\w+/, alias: 'variable', }, - }); - - insertBefore(coffeescript, 'comment', { - 'multiline-comment': { - pattern: /###[\s\S]+?###/, - alias: 'comment', - }, - - // Block regexp can contain comments and interpolation - 'block-regex': { - pattern: /\/{3}[\s\S]*?\/{3}/, - alias: 'regex', - inside: { - 'comment': comment, - 'interpolation': interpolation, - }, - }, - }); - - insertBefore(coffeescript, 'string', { - 'inline-javascript': { - pattern: /`(?:\\[\s\S]|[^\\`])*`/, - inside: { - 'delimiter': { - pattern: /^`|`$/, - alias: 'punctuation', + $insertBefore: { + 'comment': { + 'multiline-comment': { + pattern: /###[\s\S]+?###/, + alias: 'comment', }, - 'script': { - pattern: /[\s\S]+/, - alias: 'language-javascript', - inside: 'javascript', + + // Block regexp can contain comments and interpolation + 'block-regex': { + pattern: /\/{3}[\s\S]*?\/{3}/, + alias: 'regex', + inside: { + 'comment': comment, + 'interpolation': interpolation, + }, }, }, - }, + 'string': { + 'inline-javascript': { + pattern: /`(?:\\[\s\S]|[^\\`])*`/, + inside: { + 'delimiter': { + pattern: /^`|`$/, + alias: 'punctuation', + }, + 'script': { + pattern: /[\s\S]+/, + alias: 'language-javascript', + inside: 'javascript', + }, + }, + }, - // Block strings - 'multiline-string': [ - { - pattern: /'''[\s\S]*?'''/, - greedy: true, - alias: 'string', + // Block strings + 'multiline-string': [ + { + pattern: /'''[\s\S]*?'''/, + greedy: true, + alias: 'string', + }, + { + pattern: /"""[\s\S]*?"""/, + greedy: true, + alias: 'string', + inside: { + 'interpolation': interpolation, + }, + }, + ], }, - { - pattern: /"""[\s\S]*?"""/, - greedy: true, - alias: 'string', - inside: { - 'interpolation': interpolation, - }, + 'keyword': { + // Object property + 'property': /(?!\d)\w+(?=\s*:(?!:))/, }, - ], - }); - - insertBefore(coffeescript, 'keyword', { - // Object property - 'property': /(?!\d)\w+(?=\s*:(?!:))/, - }); - - delete coffeescript['doc-comment']; - delete coffeescript['template-string']; - - return coffeescript; + }, + $delete: ['doc-comment', 'template-string'], + }; }, }; diff --git a/src/languages/crystal.js b/src/languages/crystal.js index 1b7782f302..4bcda5fdf1 100644 --- a/src/languages/crystal.js +++ b/src/languages/crystal.js @@ -1,5 +1,4 @@ import { toArray } from '../util/iterables.js'; -import { insertBefore } from '../util/language-util.js'; import ruby from './ruby.js'; /** @type {import('../types.d.ts').LanguageProto<'crystal'>} */ @@ -7,45 +6,6 @@ export default { id: 'crystal', base: ruby, grammar ({ base }) { - insertBefore(base, 'string-literal', { - 'attribute': { - pattern: /@\[.*?\]/, - inside: { - 'delimiter': { - pattern: /^@\[|\]$/, - alias: 'punctuation', - }, - 'attribute': { - pattern: /^(\s*)\w+/, - lookbehind: true, - alias: 'class-name', - }, - 'args': { - pattern: /\S(?:[\s\S]*\S)?/, - inside: 'crystal', - }, - }, - }, - 'expansion': { - pattern: /\{(?:\{.*?\}|%.*?%)\}/, - inside: { - 'content': { - pattern: /^(\{.)[\s\S]+(?=.\}$)/, - lookbehind: true, - inside: 'crystal', - }, - 'delimiter': { - pattern: /^\{[\{%]|[\}%]\}$/, - alias: 'operator', - }, - }, - }, - 'char': { - pattern: /'(?:[^\\\r\n]{1,2}|\\(?:.|u(?:[A-Fa-f0-9]{1,4}|\{[A-Fa-f0-9]{1,6}\})))'/, - greedy: true, - }, - }); - return { 'keyword': [ /\b(?:__DIR__|__END_LINE__|__FILE__|__LINE__|abstract|alias|annotation|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|ifdef|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|ptr|require|rescue|return|select|self|sizeof|struct|super|then|type|typeof|undef|uninitialized|union|unless|until|when|while|with|yield)\b/, @@ -56,8 +16,53 @@ export default { ], 'number': /\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[\da-fA-F_]*[\da-fA-F]|(?:\d(?:[\d_]*\d)?)(?:\.[\d_]*\d)?(?:[eE][+-]?[\d_]*\d)?)(?:_(?:[uif](?:8|16|32|64))?)?\b/, - 'operator': [/->/, ...toArray(base.operator)], + 'operator': [ + /->/, + ...toArray(/** @type {import('../types.d.ts').GrammarTokens} */ (base).operator), + ], 'punctuation': /[(){}[\].,;\\]/, + $insert: { + 'attribute': { + $before: 'string-literal', + pattern: /@\[.*?\]/, + inside: { + 'delimiter': { + pattern: /^@\[|\]$/, + alias: 'punctuation', + }, + 'attribute': { + pattern: /^(\s*)\w+/, + lookbehind: true, + alias: 'class-name', + }, + 'args': { + pattern: /\S(?:[\s\S]*\S)?/, + inside: 'crystal', + }, + }, + }, + 'expansion': { + $before: 'string-literal', + pattern: /\{(?:\{.*?\}|%.*?%)\}/, + inside: { + 'content': { + pattern: /^(\{.)[\s\S]+(?=.\}$)/, + lookbehind: true, + inside: 'crystal', + }, + 'delimiter': { + pattern: /^\{[\{%]|[\}%]\}$/, + alias: 'operator', + }, + }, + }, + 'char': { + $before: 'string-literal', + pattern: + /'(?:[^\\\r\n]{1,2}|\\(?:.|u(?:[A-Fa-f0-9]{1,4}|\{[A-Fa-f0-9]{1,6}\})))'/, + greedy: true, + }, + }, }; }, }; diff --git a/src/languages/csharp.js b/src/languages/csharp.js index 9752901eed..e5bdb300e8 100644 --- a/src/languages/csharp.js +++ b/src/languages/csharp.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @@ -36,7 +35,7 @@ export default { base: clike, optional: 'xml-doc', alias: ['cs', 'dotnet'], - grammar ({ base, getOptionalLanguage }) { + grammar ({ getOptionalLanguage }) { // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ const keywordKinds = { // keywords which represent a return or variable type @@ -112,133 +111,6 @@ export default { const regularString = /"(?:\\.|[^\\"\r\n])*"/.source; const verbatimString = /@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source; - insertBefore(base, 'number', { - 'range': { - pattern: /\.\./, - alias: 'operator', - }, - }); - - insertBefore(base, 'punctuation', { - 'named-parameter': { - pattern: re(/([(,]\s*)<<0>>(?=\s*:)/.source, [name]), - lookbehind: true, - alias: 'punctuation', - }, - }); - - insertBefore(base, 'class-name', { - 'namespace': { - // namespace Foo.Bar {} - // using Foo.Bar; - pattern: re(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source, [ - name, - ]), - lookbehind: true, - inside: { - 'punctuation': /\./, - }, - }, - 'type-expression': { - // default(Foo), typeof(Foo), sizeof(int) - pattern: re( - /(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/ - .source, - [nestedRound] - ), - lookbehind: true, - alias: 'class-name', - inside: typeInside, - }, - 'return-type': { - // Foo ForBar(); Foo IFoo.Bar() => 0 - // int this[int index] => 0; T IReadOnlyList.this[int index] => this[index]; - // int Foo => 0; int Foo { get; set } = 0; - pattern: re(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source, [ - typeExpression, - identifier, - ]), - inside: typeInside, - alias: 'class-name', - }, - 'constructor-invocation': { - // new List> { } - pattern: re(/(\bnew\s+)<<0>>(?=\s*[[({])/.source, [typeExpression]), - lookbehind: true, - inside: typeInside, - alias: 'class-name', - }, - /*'explicit-implementation': { - // int IFoo.Bar => 0; void IFoo>.Foo(); - pattern: replace(/\b<<0>>(?=\.<<1>>)/, className, methodOrPropertyDeclaration), - inside: classNameInside, - alias: 'class-name' - },*/ - 'generic-method': { - // foo() - pattern: re(/<<0>>\s*<<1>>(?=\s*\()/.source, [name, generic]), - inside: { - 'function': re(/^<<0>>/.source, [name]), - 'generic': { - pattern: RegExp(generic), - alias: 'class-name', - inside: typeInside, - }, - }, - }, - 'type-list': { - // The list of types inherited or of generic constraints - // class Foo : Bar, IList - // where F : Bar, IList - pattern: re( - /\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/ - .source, - [ - typeDeclarationKeywords, - genericName, - name, - typeExpression, - keywords.source, - nestedRound, - /\bnew\s*\(\s*\)/.source, - ] - ), - lookbehind: true, - inside: { - 'record-arguments': { - pattern: re(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source, [ - genericName, - nestedRound, - ]), - lookbehind: true, - greedy: true, - inside: 'csharp', - }, - 'keyword': keywords, - 'class-name': { - pattern: RegExp(typeExpression), - greedy: true, - inside: typeInside, - }, - 'punctuation': /[,()]/, - }, - }, - 'preprocessor': { - pattern: /(^[\t ]*)#.*/m, - lookbehind: true, - alias: 'property', - inside: { - // highlight preprocessor directives as keywords - 'directive': { - pattern: - /(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/, - lookbehind: true, - alias: 'keyword', - }, - }, - }, - }); - // attributes const regularStringOrCharacter = regularString + '|' + character; const regularStringCharacterOrComment = replace( @@ -255,37 +127,6 @@ export default { .source; const attr = replace(/<<0>>(?:\s*\(<<1>>*\))?/.source, [identifier, roundExpression]); - insertBefore(base, 'class-name', { - 'attribute': { - // Attributes - // [Foo], [Foo(1), Bar(2, Prop = "foo")], [return: Foo(1), Bar(2)], [assembly: Foo(Bar)] - pattern: re( - /((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/ - .source, - [attrTarget, attr] - ), - lookbehind: true, - greedy: true, - inside: { - 'target': { - pattern: re(/^<<0>>(?=\s*:)/.source, [attrTarget]), - alias: 'keyword', - }, - 'attribute-arguments': { - pattern: re(/\(<<0>>*\)/.source, [roundExpression]), - inside: 'csharp', - }, - 'class-name': { - pattern: RegExp(identifier), - inside: { - 'punctuation': /\./, - }, - }, - 'punctuation': /[:,]/, - }, - }, - }); - // string interpolation const formatString = /:[^}\r\n]+/.source; // multi line @@ -337,36 +178,6 @@ export default { }; } - insertBefore(base, 'string', { - 'interpolation-string': [ - { - pattern: re( - /(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source, - [mInterpolation] - ), - lookbehind: true, - greedy: true, - inside: createInterpolationInside(mInterpolation, mInterpolationRound), - }, - { - pattern: re(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source, [ - sInterpolation, - ]), - lookbehind: true, - greedy: true, - inside: createInterpolationInside(sInterpolation, sInterpolationRound), - }, - ], - 'char': { - pattern: RegExp(character), - greedy: true, - }, - }); - - insertBefore(base, 'comment', { - 'doc-comment': getOptionalLanguage('xml-doc')?.slash, - }); - return { 'string': [ { @@ -453,6 +264,191 @@ export default { /(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i, 'operator': />>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/, 'punctuation': /\?\.?|::|[{}[\];(),.:]/, + $insertBefore: { + 'number': { + 'range': { + pattern: /\.\./, + alias: 'operator', + }, + }, + 'punctuation': { + 'named-parameter': { + pattern: re(/([(,]\s*)<<0>>(?=\s*:)/.source, [name]), + lookbehind: true, + alias: 'punctuation', + }, + }, + 'class-name': { + 'namespace': { + // namespace Foo.Bar {} + // using Foo.Bar; + pattern: re( + /(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source, + [name] + ), + lookbehind: true, + inside: { + 'punctuation': /\./, + }, + }, + 'type-expression': { + // default(Foo), typeof(Foo), sizeof(int) + pattern: re( + /(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/ + .source, + [nestedRound] + ), + lookbehind: true, + alias: 'class-name', + inside: typeInside, + }, + 'return-type': { + // Foo ForBar(); Foo IFoo.Bar() => 0 + // int this[int index] => 0; T IReadOnlyList.this[int index] => this[index]; + // int Foo => 0; int Foo { get; set } = 0; + pattern: re( + /<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source, + [typeExpression, identifier] + ), + inside: typeInside, + alias: 'class-name', + }, + 'constructor-invocation': { + // new List> { } + pattern: re(/(\bnew\s+)<<0>>(?=\s*[[({])/.source, [typeExpression]), + lookbehind: true, + inside: typeInside, + alias: 'class-name', + }, + /*'explicit-implementation': { + // int IFoo.Bar => 0; void IFoo>.Foo(); + pattern: replace(/\b<<0>>(?=\.<<1>>)/, className, methodOrPropertyDeclaration), + inside: classNameInside, + alias: 'class-name' + },*/ + 'generic-method': { + // foo() + pattern: re(/<<0>>\s*<<1>>(?=\s*\()/.source, [name, generic]), + inside: { + 'function': re(/^<<0>>/.source, [name]), + 'generic': { + pattern: RegExp(generic), + alias: 'class-name', + inside: typeInside, + }, + }, + }, + 'type-list': { + // The list of types inherited or of generic constraints + // class Foo : Bar, IList + // where F : Bar, IList + pattern: re( + /\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/ + .source, + [ + typeDeclarationKeywords, + genericName, + name, + typeExpression, + keywords.source, + nestedRound, + /\bnew\s*\(\s*\)/.source, + ] + ), + lookbehind: true, + inside: { + 'record-arguments': { + pattern: re(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source, [ + genericName, + nestedRound, + ]), + lookbehind: true, + greedy: true, + inside: 'csharp', + }, + 'keyword': keywords, + 'class-name': { + pattern: RegExp(typeExpression), + greedy: true, + inside: typeInside, + }, + 'punctuation': /[,()]/, + }, + }, + 'preprocessor': { + pattern: /(^[\t ]*)#.*/m, + lookbehind: true, + alias: 'property', + inside: { + // highlight preprocessor directives as keywords + 'directive': { + pattern: + /(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/, + lookbehind: true, + alias: 'keyword', + }, + }, + }, + 'attribute': { + // Attributes + // [Foo], [Foo(1), Bar(2, Prop = "foo")], [return: Foo(1), Bar(2)], [assembly: Foo(Bar)] + pattern: re( + /((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/ + .source, + [attrTarget, attr] + ), + lookbehind: true, + greedy: true, + inside: { + 'target': { + pattern: re(/^<<0>>(?=\s*:)/.source, [attrTarget]), + alias: 'keyword', + }, + 'attribute-arguments': { + pattern: re(/\(<<0>>*\)/.source, [roundExpression]), + inside: 'csharp', + }, + 'class-name': { + pattern: RegExp(identifier), + inside: { + 'punctuation': /\./, + }, + }, + 'punctuation': /[:,]/, + }, + }, + }, + 'string': { + 'interpolation-string': [ + { + pattern: re( + /(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source, + [mInterpolation] + ), + lookbehind: true, + greedy: true, + inside: createInterpolationInside(mInterpolation, mInterpolationRound), + }, + { + pattern: re(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source, [ + sInterpolation, + ]), + lookbehind: true, + greedy: true, + inside: createInterpolationInside(sInterpolation, sInterpolationRound), + }, + ], + 'char': { + pattern: RegExp(character), + greedy: true, + }, + }, + 'comment': { + 'doc-comment': /** @type {import('../types.d.ts').GrammarTokens} */ ( + getOptionalLanguage('xml-doc') + )?.slash, + }, + }, }; }, }; diff --git a/src/languages/cshtml.js b/src/languages/cshtml.js index 15ceddf0e1..30d03e44d5 100644 --- a/src/languages/cshtml.js +++ b/src/languages/cshtml.js @@ -5,7 +5,8 @@ import markup from './markup.js'; /** @type {import('../types.d.ts').LanguageProto<'cshtml'>} */ export default { id: 'cshtml', - require: [markup, csharp], + base: markup, + require: csharp, alias: 'razor', grammar ({ extend }) { // Docs: @@ -138,8 +139,6 @@ export default { // In the below code, both CSHTML and C#+HTML will be create as separate language definitions that reference each // other. However, only CSHTML will be exported via `Prism.languages`. - const cshtml = extend('markup', {}); - const csharpWithHtml = extend('csharp', {}); insertBefore(csharpWithHtml, 'string', { 'html': { @@ -166,104 +165,104 @@ export default { }, }; - const tag = /** @type {GrammarToken} */ (cshtml.tag); - tag.pattern = RegExp(/<\/?/.source + tagContent); - const attrValue = /** @type {GrammarToken} */ ( - /** @type {Grammar} */ (tag.inside)['attr-value'] - ); - attrValue.pattern = RegExp(/=\s*/.source + tagAttrValue); - insertBefore(/** @type {Grammar} */ (attrValue.inside), 'punctuation', { - 'value': inlineValue, - }); - - insertBefore(cshtml, 'prolog', { - 'razor-comment': { - pattern: /@\*[\s\S]*?\*@/, - greedy: true, - alias: 'comment', + return { + $merge: { + 'tag': { + pattern: RegExp(/<\/?/.source + tagContent), + inside: { + 'attr-value': { + pattern: RegExp(/=\s*/.source + tagAttrValue), + }, + }, + }, }, + $insertBefore: { + 'tag/attr-value/punctuation': { + 'value': inlineValue, + }, + 'prolog': { + 'razor-comment': { + pattern: /@\*[\s\S]*?\*@/, + greedy: true, + alias: 'comment', + }, - 'block': { - pattern: RegExp( - /(^|[^@])@/.source + - '(?:' + - [ - // @{ ... } - curly, - // @code{ ... } - /(?:code|functions)\s*/.source + curly, - // @for (...) { ... } - /(?:for|foreach|lock|switch|using|while)\s*/.source + - round + - /\s*/.source + - curly, - // @do { ... } while (...); - /do\s*/.source + - curly + - /\s*while\s*/.source + - round + - /(?:\s*;)?/.source, - // @try { ... } catch (...) { ... } finally { ... } - /try\s*/.source + - curly + - /\s*catch\s*/.source + - round + - /\s*/.source + - curly + - /\s*finally\s*/.source + - curly, - // @if (...) {...} else if (...) {...} else {...} - /if\s*/.source + - round + - /\s*/.source + - curly + - '(?:' + - /\s*else/.source + + 'block': { + pattern: RegExp( + /(^|[^@])@/.source + '(?:' + - /\s+if\s*/.source + - round + - ')?' + - /\s*/.source + - curly + - ')*', - // @helper Ident(params) { ... } - /helper\s+\w+\s*/.source + round + /\s*/.source + curly, - ].join('|') + - ')' - ), - lookbehind: true, - greedy: true, - inside: { - 'keyword': /^@\w*/, - 'csharp': cs, - }, - }, + [ + // @{ ... } + curly, + // @code{ ... } + /(?:code|functions)\s*/.source + curly, + // @for (...) { ... } + /(?:for|foreach|lock|switch|using|while)\s*/.source + + round + + /\s*/.source + + curly, + // @do { ... } while (...); + /do\s*/.source + + curly + + /\s*while\s*/.source + + round + + /(?:\s*;)?/.source, + // @try { ... } catch (...) { ... } finally { ... } + /try\s*/.source + + curly + + /\s*catch\s*/.source + + round + + /\s*/.source + + curly + + /\s*finally\s*/.source + + curly, + // @if (...) {...} else if (...) {...} else {...} + /if\s*/.source + + round + + /\s*/.source + + curly + + '(?:' + + /\s*else/.source + + '(?:' + + /\s+if\s*/.source + + round + + ')?' + + /\s*/.source + + curly + + ')*', + // @helper Ident(params) { ... } + /helper\s+\w+\s*/.source + round + /\s*/.source + curly, + ].join('|') + + ')' + ), + lookbehind: true, + greedy: true, + inside: { + 'keyword': /^@\w*/, + 'csharp': cs, + }, + }, - 'directive': { - pattern: - /^([ \t]*)@(?:addTagHelper|attribute|implements|inherits|inject|layout|model|namespace|page|preservewhitespace|removeTagHelper|section|tagHelperPrefix|using)(?=\s).*/m, - lookbehind: true, - greedy: true, - inside: { - 'keyword': /^@\w+/, - 'csharp': cs, - }, - }, + 'directive': { + pattern: + /^([ \t]*)@(?:addTagHelper|attribute|implements|inherits|inject|layout|model|namespace|page|preservewhitespace|removeTagHelper|section|tagHelperPrefix|using)(?=\s).*/m, + lookbehind: true, + greedy: true, + inside: { + 'keyword': /^@\w+/, + 'csharp': cs, + }, + }, - 'value': inlineValue, + 'value': inlineValue, - 'delegate-operator': { - pattern: /(^|[^@])@(?=<)/, - lookbehind: true, - alias: 'operator', + 'delegate-operator': { + pattern: /(^|[^@])@(?=<)/, + lookbehind: true, + alias: 'operator', + }, + }, }, - }); - - return cshtml; + }; }, }; - -/** - * @typedef {import('../types.d.ts').GrammarToken} GrammarToken - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/css.js b/src/languages/css.js index c70e8c1de5..73bf09679b 100644 --- a/src/languages/css.js +++ b/src/languages/css.js @@ -1,5 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; - /** @type {import('../types.d.ts').LanguageProto<'css'>} */ export default { id: 'css', @@ -8,8 +6,7 @@ export default { const string = /(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/; - /** @type {Grammar} */ - const css = { + return { 'comment': /\/\*[\s\S]*?\*\//, 'atrule': { pattern: RegExp( @@ -20,7 +17,7 @@ export default { ')*?' + /(?:;|(?=\s*\{))/.source ), - inside: /** @type {Grammar} */ ({ + inside: { 'rule': /^@[\w-]+/, 'selector-function-argument': { pattern: @@ -33,8 +30,8 @@ export default { pattern: /(^|[^\w-])(?:and|not|only|or)(?![\w-])/, lookbehind: true, }, - $rest: /** @type {Grammar['$rest']} */ ('css'), - }), + $rest: 'css', + }, }, 'url': { // https://drafts.csswg.org/css-values-3/#urls @@ -85,21 +82,11 @@ export default { lookbehind: true, }, 'punctuation': /[(){};:,]/, + $insertBefore: { + 'function': /** @type {import('../types.d.ts').GrammarTokens} */ ( + getOptionalLanguage('css-extras') + ), + }, }; - - const extras = getOptionalLanguage('css-extras'); - if (extras) { - insertBefore( - css, - 'function', - /** @type {import('../types.d.ts').GrammarTokens} */ (extras) - ); - } - - return css; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/d.js b/src/languages/d.js index 7022de37ae..eafd837057 100644 --- a/src/languages/d.js +++ b/src/languages/d.js @@ -1,30 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'d'>} */ export default { id: 'd', base: clike, - grammar ({ base }) { - insertBefore(base, 'string', { - // Characters - // 'a', '\\', '\n', '\xFF', '\377', '\uFFFF', '\U0010FFFF', '\quot' - 'char': /'(?:\\(?:\W|\w+)|[^\\])'/, - }); - - insertBefore(base, 'keyword', { - 'property': /\B@\w*/, - }); - - insertBefore(base, 'function', { - 'register': { - // Iasm registers - pattern: - /\b(?:[ABCD][LHX]|E?(?:BP|DI|SI|SP)|[BS]PL|CR[0234]|[ECSDGF]S|[DS]IL|DR[012367]|E[ABCD]X|X?MM[0-7]|R(?:1[0-5]|[89])[BWD]?|R[ABCD]X|R[BS]P|R[DS]I|TR[3-7]|XMM(?:1[0-5]|[89])|YMM(?:1[0-5]|\d))\b|\bST(?:\([0-7]\)|\b)/, - alias: 'variable', - }, - }); - + grammar () { return { 'comment': [ { @@ -101,6 +81,25 @@ export default { 'operator': /\|[|=]?|&[&=]?|\+[+=]?|-[-=]?|\.?\.\.|=[>=]?|!(?:i[ns]\b|<>?=?|>=?|=)?|\bi[ns]\b|(?:<[<>]?|>>?>?|\^\^|[*\/%^~])=?/, + + $insertBefore: { + 'string': { + // Characters + // 'a', '\\', '\n', '\xFF', '\377', '\uFFFF', '\U0010FFFF', '\quot' + 'char': /'(?:\\(?:\W|\w+)|[^\\])'/, + }, + 'keyword': { + 'property': /\B@\w*/, + }, + 'function': { + 'register': { + // Iasm registers + pattern: + /\b(?:[ABCD][LHX]|E?(?:BP|DI|SI|SP)|[BS]PL|CR[0234]|[ECSDGF]S|[DS]IL|DR[012367]|E[ABCD]X|X?MM[0-7]|R(?:1[0-5]|[89])[BWD]?|R[ABCD]X|R[BS]P|R[DS]I|TR[3-7]|XMM(?:1[0-5]|[89])|YMM(?:1[0-5]|\d))\b|\bST(?:\([0-7]\)|\b)/, + alias: 'variable', + }, + }, + }, }; }, }; diff --git a/src/languages/dart.js b/src/languages/dart.js index 3b05efd0d3..80856b0c76 100644 --- a/src/languages/dart.js +++ b/src/languages/dart.js @@ -1,11 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'dart'>} */ export default { id: 'dart', base: clike, - grammar ({ base }) { + grammar () { const keywords = [ /\b(?:async|sync|yield)\*/, /\b(?:abstract|assert|async|await|break|case|catch|class|const|continue|covariant|default|deferred|do|dynamic|else|enum|export|extends|extension|external|factory|final|finally|for|get|hide|if|implements|import|in|interface|library|mixin|new|null|on|operator|part|rethrow|return|set|show|static|super|switch|sync|this|throw|try|typedef|var|void|while|with|yield)\b/, @@ -28,47 +27,6 @@ export default { }, }; - insertBefore(base, 'string', { - 'string-literal': { - pattern: /r?(?:("""|''')[\s\S]*?\1|(["'])(?:\\.|(?!\2)[^\\\r\n])*\2(?!\2))/, - greedy: true, - inside: { - 'interpolation': { - pattern: /((?:^|[^\\])(?:\\{2})*)\$(?:\w+|\{(?:[^{}]|\{[^{}]*\})*\})/, - lookbehind: true, - inside: { - 'punctuation': /^\$\{?|\}$/, - 'expression': { - pattern: /[\s\S]+/, - inside: 'dart', - }, - }, - }, - 'string': /[\s\S]+/, - }, - }, - 'string': undefined, - }); - - insertBefore(base, 'class-name', { - 'metadata': { - pattern: /@\w+/, - alias: 'function', - }, - }); - - insertBefore(base, 'class-name', { - 'generics': { - pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/, - inside: { - 'class-name': className, - 'keyword': keywords, - 'punctuation': /[<>(),.:]/, - 'operator': /[?&|]/, - }, - }, - }); - return { 'class-name': [ className, @@ -83,6 +41,46 @@ export default { 'keyword': keywords, 'operator': /\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/, + $insertBefore: { + 'string': { + 'string-literal': { + pattern: /r?(?:("""|''')[\s\S]*?\1|(["'])(?:\\.|(?!\2)[^\\\r\n])*\2(?!\2))/, + greedy: true, + inside: { + 'interpolation': { + pattern: + /((?:^|[^\\])(?:\\{2})*)\$(?:\w+|\{(?:[^{}]|\{[^{}]*\})*\})/, + lookbehind: true, + inside: { + 'punctuation': /^\$\{?|\}$/, + 'expression': { + pattern: /[\s\S]+/, + inside: 'dart', + }, + }, + }, + 'string': /[\s\S]+/, + }, + }, + 'string': undefined, + }, + 'class-name': { + 'metadata': { + pattern: /@\w+/, + alias: 'function', + }, + 'generics': { + pattern: + /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/, + inside: { + 'class-name': className, + 'keyword': keywords, + 'punctuation': /[<>(),.:]/, + 'operator': /[?&|]/, + }, + }, + }, + }, }; }, }; diff --git a/src/languages/django.js b/src/languages/django.js index 64446061ec..555ea27aaf 100644 --- a/src/languages/django.js +++ b/src/languages/django.js @@ -9,7 +9,7 @@ export default { id: 'django', require: markup, alias: 'jinja2', - grammar: /** @type {Grammar} */ ({ + grammar: { 'django': { pattern: /\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}|\{#[\s\S]*?#\}/, inside: { @@ -47,10 +47,6 @@ export default { 'punctuation': /[{}[\](),.:;]/, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }), + $tokenize: embeddedIn('markup'), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/ejs.js b/src/languages/ejs.js index 13da46bfba..f97a6170a5 100644 --- a/src/languages/ejs.js +++ b/src/languages/ejs.js @@ -7,7 +7,7 @@ export default { id: 'ejs', require: [javascript, markup], alias: 'eta', - grammar: /** @type {Grammar} */ ({ + grammar: { 'ejs-comment': { pattern: /<%#[\s\S]*?%>/, greedy: true, @@ -30,10 +30,6 @@ export default { }, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }), + $tokenize: embeddedIn('markup'), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/elixir.js b/src/languages/elixir.js index 96f0b167aa..bef26b3df1 100644 --- a/src/languages/elixir.js +++ b/src/languages/elixir.js @@ -2,7 +2,7 @@ export default { id: 'elixir', grammar () { - const stringInside = /** @type {Grammar} */ ({ + const stringInside = { 'interpolation': { pattern: /#\{[^}]+\}/, inside: { @@ -10,10 +10,10 @@ export default { pattern: /^#\{|\}$/, alias: 'punctuation', }, - $rest: /** @type {Grammar['$rest']} */ ('elixir'), + $rest: 'elixir', }, }, - }); + }; return { 'doc': { @@ -98,7 +98,3 @@ export default { }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/erb.js b/src/languages/erb.js index 4463fd8bec..6f893c3f1b 100644 --- a/src/languages/erb.js +++ b/src/languages/erb.js @@ -6,7 +6,7 @@ import ruby from './ruby.js'; export default { id: 'erb', require: [ruby, markup], - grammar: /** @type {Grammar} */ ({ + grammar: { 'erb': { pattern: /<%=?(?:[^\r\n]|[\r\n](?!=begin)|[\r\n]=begin\s(?:[^\r\n]|[\r\n](?!=end))*[\r\n]=end)+?%>/, @@ -22,10 +22,6 @@ export default { }, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }), + $tokenize: embeddedIn('markup'), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/etlua.js b/src/languages/etlua.js index e0c4da0208..7b43f4660a 100644 --- a/src/languages/etlua.js +++ b/src/languages/etlua.js @@ -6,7 +6,7 @@ import markup from './markup.js'; export default { id: 'etlua', require: [lua, markup], - grammar: /** @type {Grammar} */ ({ + grammar: { 'etlua': { pattern: /<%[\s\S]+?%>/, inside: { @@ -20,10 +20,6 @@ export default { }, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }), + $tokenize: embeddedIn('markup'), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/firestore-security-rules.js b/src/languages/firestore-security-rules.js index 59b6d922e4..1d16e57b9c 100644 --- a/src/languages/firestore-security-rules.js +++ b/src/languages/firestore-security-rules.js @@ -1,46 +1,45 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'firestore-security-rules'>} */ export default { id: 'firestore-security-rules', base: clike, - grammar ({ base }) { - delete base['class-name']; - - insertBefore(base, 'keyword', { - 'path': { - pattern: - /(^|[\s(),])(?:\/(?:[\w\xA0-\uFFFF]+|\{[\w\xA0-\uFFFF]+(?:=\*\*)?\}|\$\([\w\xA0-\uFFFF.]+\)))+/, - lookbehind: true, - greedy: true, - inside: { - 'variable': { - pattern: /\{[\w\xA0-\uFFFF]+(?:=\*\*)?\}|\$\([\w\xA0-\uFFFF.]+\)/, - inside: { - 'operator': /=/, - 'keyword': /\*\*/, - 'punctuation': /[.$(){}]/, + grammar () { + return { + 'comment': /\/\/.*/, + 'keyword': /\b(?:allow|function|if|match|null|return|rules_version|service)\b/, + 'operator': /&&|\|\||[<>!=]=?|[-+*/%]|\b(?:in|is)\b/, + $insert: { + 'path': { + $before: 'keyword', + pattern: + /(^|[\s(),])(?:\/(?:[\w\xA0-\uFFFF]+|\{[\w\xA0-\uFFFF]+(?:=\*\*)?\}|\$\([\w\xA0-\uFFFF.]+\)))+/, + lookbehind: true, + greedy: true, + inside: { + 'variable': { + pattern: /\{[\w\xA0-\uFFFF]+(?:=\*\*)?\}|\$\([\w\xA0-\uFFFF.]+\)/, + inside: { + 'operator': /=/, + 'keyword': /\*\*/, + 'punctuation': /[.$(){}]/, + }, }, + 'punctuation': /\//, }, - 'punctuation': /\//, }, - }, - 'method': { - // to make the pattern shorter, the actual method names are omitted - pattern: /(\ballow\s+)[a-z]+(?:\s*,\s*[a-z]+)*(?=\s*[:;])/, - lookbehind: true, - alias: 'builtin', - inside: { - 'punctuation': /,/, + 'method': { + $before: 'keyword', + // to make the pattern shorter, the actual method names are omitted + pattern: /(\ballow\s+)[a-z]+(?:\s*,\s*[a-z]+)*(?=\s*[:;])/, + lookbehind: true, + alias: 'builtin', + inside: { + 'punctuation': /,/, + }, }, }, - }); - - return { - 'comment': /\/\/.*/, - 'keyword': /\b(?:allow|function|if|match|null|return|rules_version|service)\b/, - 'operator': /&&|\|\||[<>!=]=?|[-+*/%]|\b(?:in|is)\b/, + $delete: ['class-name'], }; }, }; diff --git a/src/languages/flow.js b/src/languages/flow.js index e77ed73659..eba3d6e5db 100644 --- a/src/languages/flow.js +++ b/src/languages/flow.js @@ -1,5 +1,4 @@ import { toArray } from '../util/iterables.js'; -import { insertBefore } from '../util/language-util.js'; import javascript from './javascript.js'; /** @type {import('../types.d.ts').LanguageProto<'flow'>} */ @@ -7,29 +6,12 @@ export default { id: 'flow', base: javascript, grammar ({ base }) { - insertBefore(base, 'keyword', { - 'type': { - pattern: - /\b(?:[Bb]oolean|Function|[Nn]umber|[Ss]tring|[Ss]ymbol|any|mixed|null|void)\b/, - alias: 'class-name', - }, - }); - - insertBefore(base, 'operator', { - 'flow-punctuation': { - pattern: /\{\||\|\}/, - alias: 'punctuation', - }, - }); - const fnVariable = /** @type {import('../types.d.ts').GrammarToken} */ ( base['function-variable'] ); fnVariable.pattern = /(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=\s*(?:function\b|(?:\([^()]*\)(?:\s*:\s*\w+)?|(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/i; - delete base['parameter']; - return { 'keyword': [ { @@ -41,8 +23,22 @@ export default { /(^|[^$]\B)\$(?:Diff|Enum|Exact|Keys|ObjMap|PropertyType|Record|Shape|Subtype|Supertype|await)\b(?!\$)/, lookbehind: true, }, - ...toArray(base.keyword), + ...toArray(/** @type {import('../types.d.ts').GrammarTokens} */ (base).keyword), ], + $insert: { + 'type': { + $before: 'keyword', + pattern: + /\b(?:[Bb]oolean|Function|[Nn]umber|[Ss]tring|[Ss]ymbol|any|mixed|null|void)\b/, + alias: 'class-name', + }, + 'flow-punctuation': { + $before: 'operator', + pattern: /\{\||\|\}/, + alias: 'punctuation', + }, + }, + $delete: ['parameter'], }; }, }; diff --git a/src/languages/fsharp.js b/src/languages/fsharp.js index af094960d4..4b45e634a2 100644 --- a/src/languages/fsharp.js +++ b/src/languages/fsharp.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'fsharp'>} */ @@ -6,54 +5,7 @@ export default { id: 'fsharp', base: clike, optional: 'xml-doc', - grammar ({ base, getOptionalLanguage }) { - insertBefore(base, 'keyword', { - 'preprocessor': { - pattern: /(^[\t ]*)#.*/m, - lookbehind: true, - alias: 'property', - inside: { - 'directive': { - pattern: /(^#)\b(?:else|endif|if|light|line|nowarn)\b/, - lookbehind: true, - alias: 'keyword', - }, - }, - }, - }); - insertBefore(base, 'punctuation', { - 'computation-expression': { - pattern: /\b[_a-z]\w*(?=\s*\{)/i, - alias: 'keyword', - }, - }); - insertBefore(base, 'string', { - 'annotation': { - pattern: /\[<.+?>\]/, - greedy: true, - inside: { - 'punctuation': /^\[<|>\]$/, - 'class-name': { - pattern: /^\w+$|(^|;\s*)[A-Z]\w*(?=\()/, - lookbehind: true, - }, - 'annotation-content': { - pattern: /[\s\S]+/, - inside: 'fsharp', - }, - }, - }, - 'char': { - pattern: - /'(?:[^\\']|\\(?:.|\d{3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}|U[a-fA-F\d]{8}))'B?/, - greedy: true, - }, - }); - - insertBefore(base, 'comment', { - 'doc-comment': getOptionalLanguage('xml-doc')?.slash, - }); - + grammar ({ getOptionalLanguage }) { return { 'comment': [ { @@ -90,6 +42,55 @@ export default { ], 'operator': /([<>~&^])\1\1|([*.:<>&])\2|<-|->|[!=:]=|?|\??(?:<=|>=|<>|[-+*/%=<>])\??|[!?^&]|~[+~-]|:>|:\?>?/, + $insert: { + 'preprocessor': { + $before: 'keyword', + pattern: /(^[\t ]*)#.*/m, + lookbehind: true, + alias: 'property', + inside: { + 'directive': { + pattern: /(^#)\b(?:else|endif|if|light|line|nowarn)\b/, + lookbehind: true, + alias: 'keyword', + }, + }, + }, + 'computation-expression': { + $before: 'punctuation', + pattern: /\b[_a-z]\w*(?=\s*\{)/i, + alias: 'keyword', + }, + 'annotation': { + $before: 'string', + pattern: /\[<.+?>\]/, + greedy: true, + inside: { + 'punctuation': /^\[<|>\]$/, + 'class-name': { + pattern: /^\w+$|(^|;\s*)[A-Z]\w*(?=\()/, + lookbehind: true, + }, + 'annotation-content': { + pattern: /[\s\S]+/, + inside: 'fsharp', + }, + }, + }, + 'char': { + $before: 'string', + pattern: + /'(?:[^\\']|\\(?:.|\d{3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}|U[a-fA-F\d]{8}))'B?/, + greedy: true, + }, + }, + $insertBefore: { + 'comment': { + 'doc-comment': /** @type {import('../types.d.ts').GrammarTokens} */ ( + getOptionalLanguage('xml-doc') + )?.slash, + }, + }, }; }, }; diff --git a/src/languages/ftl.js b/src/languages/ftl.js index 76f4211f7d..cfb3a675b9 100644 --- a/src/languages/ftl.js +++ b/src/languages/ftl.js @@ -72,7 +72,7 @@ export default { stringInterpolation ).inside.interpolation.inside.$rest = ftl; - return /** @type {Grammar} */ ({ + return { 'ftl-comment': { // the pattern is shortened to be more efficient pattern: /<#--[\s\S]*?-->/, @@ -116,8 +116,8 @@ export default { }, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }); + $tokenize: embeddedIn('markup'), + }; }, }; diff --git a/src/languages/go.js b/src/languages/go.js index c59c06a12f..4daaa0576f 100644 --- a/src/languages/go.js +++ b/src/languages/go.js @@ -1,20 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'go'>} */ export default { id: 'go', base: clike, - grammar ({ base }) { - insertBefore(base, 'string', { - 'char': { - pattern: /'(?:\\.|[^'\\\r\n]){0,10}'/, - greedy: true, - }, - }); - - delete base['class-name']; - + grammar () { return { 'string': { pattern: /(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/, @@ -36,6 +26,14 @@ export default { /[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./, 'builtin': /\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|imag|u?int(?:8|16|32|64)?|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/, + $insert: { + 'char': { + $before: 'string', + pattern: /'(?:\\.|[^'\\\r\n]){0,10}'/, + greedy: true, + }, + }, + $delete: ['class-name'], }; }, }; diff --git a/src/languages/gradle.js b/src/languages/gradle.js index a2b0f0dd5e..a05f413e15 100644 --- a/src/languages/gradle.js +++ b/src/languages/gradle.js @@ -1,11 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'gradle'>} */ export default { id: 'gradle', base: clike, - grammar ({ base }) { + grammar () { const interpolation = { pattern: /((?:^|[^\\$])(?:\\{2})*)\$(?:\w+|\{[^{}]*\})/, lookbehind: true, @@ -21,35 +20,6 @@ export default { }, }; - insertBefore(base, 'string', { - 'shebang': { - pattern: /#!.+/, - alias: 'comment', - greedy: true, - }, - 'interpolation-string': { - pattern: - /"""(?:[^\\]|\\[\s\S])*?"""|(["/])(?:\\.|(?!\1)[^\\\r\n])*\1|\$\/(?:[^/$]|\$(?:[/$]|(?![/$]))|\/(?!\$))*\/\$/, - greedy: true, - inside: { - 'interpolation': interpolation, - 'string': /[\s\S]+/, - }, - }, - }); - - insertBefore(base, 'punctuation', { - 'spock-block': /\b(?:and|cleanup|expect|given|setup|then|when|where):/, - }); - - insertBefore(base, 'function', { - 'annotation': { - pattern: /(^|[^.])@\w+/, - lookbehind: true, - alias: 'punctuation', - }, - }); - return { 'string': { pattern: /'''(?:[^\\]|\\[\s\S])*?'''|'(?:\\.|[^\\'\r\n])*'/, @@ -65,6 +35,34 @@ export default { lookbehind: true, }, 'punctuation': /\.+|[{}[\];(),:$]/, + $insert: { + 'shebang': { + $before: 'string', + pattern: /#!.+/, + alias: 'comment', + greedy: true, + }, + 'interpolation-string': { + $before: 'string', + pattern: + /"""(?:[^\\]|\\[\s\S])*?"""|(["/])(?:\\.|(?!\1)[^\\\r\n])*\1|\$\/(?:[^/$]|\$(?:[/$]|(?![/$]))|\/(?!\$))*\/\$/, + greedy: true, + inside: { + 'interpolation': interpolation, + 'string': /[\s\S]+/, + }, + }, + 'spock-block': { + $before: 'punctuation', + pattern: /\b(?:and|cleanup|expect|given|setup|then|when|where):/, + }, + 'annotation': { + $before: 'function', + pattern: /(^|[^.])@\w+/, + lookbehind: true, + alias: 'punctuation', + }, + }, }; }, }; diff --git a/src/languages/graphql.js b/src/languages/graphql.js index f022775a47..5067293c56 100644 --- a/src/languages/graphql.js +++ b/src/languages/graphql.js @@ -3,7 +3,7 @@ import { withoutTokenize } from '../util/language-util.js'; /** @type {import('../types.d.ts').LanguageProto<'graphql'>} */ export default { id: 'graphql', - grammar: /** @type {Grammar} */ ({ + grammar: { 'comment': /#.*/, 'description': { pattern: /(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i, @@ -212,7 +212,7 @@ export default { return tokens; }, - }), + }, }; /** diff --git a/src/languages/groovy.js b/src/languages/groovy.js index 250227f362..39bb4b509c 100644 --- a/src/languages/groovy.js +++ b/src/languages/groovy.js @@ -1,11 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'groovy'>} */ export default { id: 'groovy', base: clike, - grammar ({ base }) { + grammar () { const interpolation = { pattern: /((?:^|[^\\$])(?:\\{2})*)\$(?:\w+|\{[^{}]*\})/, lookbehind: true, @@ -21,37 +20,6 @@ export default { }, }; - insertBefore(base, 'string', { - 'shebang': { - pattern: /#!.+/, - alias: 'comment', - greedy: true, - }, - 'interpolation-string': { - // TODO: Slash strings (e.g. /foo/) can contain line breaks but this will cause a lot of trouble with - // simple division (see JS regex), so find a fix maybe? - pattern: - /"""(?:[^\\]|\\[\s\S])*?"""|(["/])(?:\\.|(?!\1)[^\\\r\n])*\1|\$\/(?:[^/$]|\$(?:[/$]|(?![/$]))|\/(?!\$))*\/\$/, - greedy: true, - inside: { - 'interpolation': interpolation, - 'string': /[\s\S]+/, - }, - }, - }); - - insertBefore(base, 'punctuation', { - 'spock-block': /\b(?:and|cleanup|expect|given|setup|then|when|where):/, - }); - - insertBefore(base, 'function', { - 'annotation': { - pattern: /(^|[^.])@\w+/, - lookbehind: true, - alias: 'punctuation', - }, - }); - return { 'string': { // https://groovy-lang.org/syntax.html#_dollar_slashy_string @@ -68,6 +36,36 @@ export default { lookbehind: true, }, 'punctuation': /\.+|[{}[\];(),:$]/, + $insert: { + 'shebang': { + $before: 'string', + pattern: /#!.+/, + alias: 'comment', + greedy: true, + }, + 'interpolation-string': { + $before: 'string', + // TODO: Slash strings (e.g. /foo/) can contain line breaks but this will cause a lot of trouble with + // simple division (see JS regex), so find a fix maybe? + pattern: + /"""(?:[^\\]|\\[\s\S])*?"""|(["/])(?:\\.|(?!\1)[^\\\r\n])*\1|\$\/(?:[^/$]|\$(?:[/$]|(?![/$]))|\/(?!\$))*\/\$/, + greedy: true, + inside: { + 'interpolation': interpolation, + 'string': /[\s\S]+/, + }, + }, + 'spock-block': { + $before: 'punctuation', + pattern: /\b(?:and|cleanup|expect|given|setup|then|when|where):/, + }, + 'annotation': { + $before: 'function', + pattern: /(^|[^.])@\w+/, + lookbehind: true, + alias: 'punctuation', + }, + }, }; }, }; diff --git a/src/languages/haml.js b/src/languages/haml.js index 1495ffe33d..63b375fcf2 100644 --- a/src/languages/haml.js +++ b/src/languages/haml.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import ruby from './ruby.js'; /** @type {import('../types.d.ts').LanguageProto<'haml'>} */ @@ -13,7 +12,43 @@ export default { code | */ - const haml = { + const filter_pattern = + '((?:^|\\r?\\n|\\r)([\\t ]*)):{{filter_name}}(?:(?:\\r?\\n|\\r)(?:\\2[\\t ].+|\\s*?(?=\\r?\\n|\\r)))+'; + + // Non exhaustive list of available filters and associated languages + const filters = [ + 'css', + { filter: 'coffee', language: 'coffeescript' }, + 'erb', + 'javascript', + 'less', + 'markdown', + 'ruby', + 'scss', + 'textile', + ]; + + const all_filters = /** @type {import('../types.d.ts').GrammarTokens} */ ({}); + for (const f of filters) { + const { filter, language } = typeof f === 'string' ? { filter: f, language: f } : f; + all_filters['filter-' + filter] = { + pattern: RegExp(filter_pattern.replace('{{filter_name}}', () => filter)), + lookbehind: true, + inside: { + 'filter-name': { + pattern: /^:[\w-]+/, + alias: 'symbol', + }, + 'text': { + pattern: /[\s\S]+/, + alias: [language, 'language-' + language], + inside: language, + }, + }, + }; + } + + return { // Multiline stuff should appear before the rest 'multiline-comment': { @@ -115,47 +150,9 @@ export default { pattern: /((?:^|\r?\n|\r)[\t ]*)[~=\-&!]+/, lookbehind: true, }, + $insertBefore: { + 'filter': all_filters, + }, }; - - const filter_pattern = - '((?:^|\\r?\\n|\\r)([\\t ]*)):{{filter_name}}(?:(?:\\r?\\n|\\r)(?:\\2[\\t ].+|\\s*?(?=\\r?\\n|\\r)))+'; - - // Non exhaustive list of available filters and associated languages - const filters = [ - 'css', - { filter: 'coffee', language: 'coffeescript' }, - 'erb', - 'javascript', - 'less', - 'markdown', - 'ruby', - 'scss', - 'textile', - ]; - - /** @type {import('../types.d.ts').Grammar} */ - const all_filters = {}; - for (const f of filters) { - const { filter, language } = typeof f === 'string' ? { filter: f, language: f } : f; - all_filters['filter-' + filter] = { - pattern: RegExp(filter_pattern.replace('{{filter_name}}', () => filter)), - lookbehind: true, - inside: { - 'filter-name': { - pattern: /^:[\w-]+/, - alias: 'symbol', - }, - 'text': { - pattern: /[\s\S]+/, - alias: [language, 'language-' + language], - inside: language, - }, - }, - }; - } - - insertBefore(haml, 'filter', all_filters); - - return haml; }, }; diff --git a/src/languages/handlebars.js b/src/languages/handlebars.js index 65c1497fbc..4ab89529c1 100644 --- a/src/languages/handlebars.js +++ b/src/languages/handlebars.js @@ -6,7 +6,7 @@ export default { id: 'handlebars', require: markup, alias: ['hbs', 'mustache'], - grammar: /** @type {Grammar} */ ({ + grammar: { 'handlebars': { pattern: /\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/, inside: { @@ -34,10 +34,6 @@ export default { 'variable': /[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }), + $tokenize: embeddedIn('markup'), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/haxe.js b/src/languages/haxe.js index 6a15448dff..5fd842654d 100644 --- a/src/languages/haxe.js +++ b/src/languages/haxe.js @@ -1,67 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'haxe'>} */ export default { id: 'haxe', base: clike, - grammar ({ base }) { - insertBefore(base, 'string', { - 'string-interpolation': { - pattern: /'(?:[^'\\]|\\[\s\S])*'/, - greedy: true, - inside: { - 'interpolation': { - pattern: /(^|[^\\])\$(?:\w+|\{[^{}]+\})/, - lookbehind: true, - inside: { - 'interpolation-punctuation': { - pattern: /^\$\{?|\}$/, - alias: 'punctuation', - }, - 'expression': { - pattern: /[\s\S]+/, - inside: 'haxe', - }, - }, - }, - 'string': /[\s\S]+/, - }, - }, - }); - - insertBefore(base, 'class-name', { - 'regex': { - pattern: /~\/(?:[^\/\\\r\n]|\\.)+\/[a-z]*/, - greedy: true, - inside: { - 'regex-flags': /\b[a-z]+$/, - 'regex-source': { - pattern: /^(~\/)[\s\S]+(?=\/$)/, - lookbehind: true, - alias: 'language-regex', - inside: 'regex', - }, - 'regex-delimiter': /^~\/|\/$/, - }, - }, - }); - - insertBefore(base, 'keyword', { - 'preprocessor': { - pattern: /#(?:else|elseif|end|if)\b.*/, - alias: 'property', - }, - 'metadata': { - pattern: /@:?[\w.]+/, - alias: 'symbol', - }, - 'reification': { - pattern: /\$(?:\w+|(?=\{))/, - alias: 'important', - }, - }); - + grammar () { return { 'string': { // Strings can be multi-line @@ -85,6 +28,61 @@ export default { greedy: true, }, 'operator': /\.{3}|\+\+|--|&&|\|\||->|=>|(?:<{1,3}|[-+*/%!=&|^])=?|[?:~]/, + $insert: { + 'string-interpolation': { + $before: 'string', + pattern: /'(?:[^'\\]|\\[\s\S])*'/, + greedy: true, + inside: { + 'interpolation': { + pattern: /(^|[^\\])\$(?:\w+|\{[^{}]+\})/, + lookbehind: true, + inside: { + 'interpolation-punctuation': { + pattern: /^\$\{?|\}$/, + alias: 'punctuation', + }, + 'expression': { + pattern: /[\s\S]+/, + inside: 'haxe', + }, + }, + }, + 'string': /[\s\S]+/, + }, + }, + 'regex': { + $before: 'class-name', + pattern: /~\/(?:[^\/\\\r\n]|\\.)+\/[a-z]*/, + greedy: true, + inside: { + 'regex-flags': /\b[a-z]+$/, + 'regex-source': { + pattern: /^(~\/)[\s\S]+(?=\/$)/, + lookbehind: true, + alias: 'language-regex', + inside: 'regex', + }, + 'regex-delimiter': /^~\/|\/$/, + }, + }, + }, + $insertBefore: { + 'keyword': { + 'preprocessor': { + pattern: /#(?:else|elseif|end|if)\b.*/, + alias: 'property', + }, + 'metadata': { + pattern: /@:?[\w.]+/, + alias: 'symbol', + }, + 'reification': { + pattern: /\$(?:\w+|(?=\{))/, + alias: 'important', + }, + }, + }, }; }, }; diff --git a/src/languages/http.js b/src/languages/http.js index cadd2f9652..97d636ded9 100644 --- a/src/languages/http.js +++ b/src/languages/http.js @@ -1,5 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; - /** @type {import('../types.d.ts').LanguageProto<'http'>} */ export default { id: 'http', @@ -13,7 +11,64 @@ export default { return RegExp('(^(?:' + name + '):[ \t]*(?![ \t]))[^]+', 'i'); } - const http = { + // Create a mapping of Content-Type headers to language definitions + const httpLanguages = { + 'application/javascript': 'javascript', + 'application/json': getOptionalLanguage('json') || 'javascript', + 'application/xml': 'xml', + 'text/xml': 'xml', + 'text/html': 'html', + 'text/css': 'css', + 'text/plain': 'plain', + }; + + // Declare which types can also be suffixes + const suffixTypes = { + 'application/json': true, + 'application/xml': true, + }; + + /** + * Returns a pattern for the given content type which matches it and any type which has it as a suffix. + * + * @param {string} contentType + * @returns {string} + */ + function getSuffixPattern (contentType) { + const suffix = contentType.replace(/^[a-z]+\//, ''); + const suffixPattern = '\\w+/(?:[\\w.-]+\\+)+' + suffix + '(?![+\\w.-])'; + return '(?:' + contentType + '|' + suffixPattern + ')'; + } + + // Insert each content type parser that has its associated language + // currently loaded. + + const options = /** @type {import('../types.d.ts').GrammarTokens} */ ({}); + for (const key in httpLanguages) { + const contentType = key; + + const pattern = suffixTypes[contentType] ? getSuffixPattern(contentType) : contentType; + options[contentType.replace(/\//g, '-')] = { + pattern: RegExp( + '(' + + /content-type:\s*/.source + + pattern + + /(?:(?:\r\n?|\n)[\w-].*)*(?:\r(?:\n|(?!\n))|\n)/.source + + ')' + + // This is a little interesting: + // The HTTP format spec required 1 empty line before the body to make everything unambiguous. + // However, when writing code by hand (e.g. to display on a website) people can forget about this, + // so we want to be liberal here. We will allow the empty line to be omitted if the first line of + // the body does not start with a [\w-] character (as headers do). + /[^ \t\w-][\s\S]*/.source, + 'i' + ), + lookbehind: true, + inside: httpLanguages[contentType], + }; + } + + return { 'request-line': { pattern: /^(?:CONNECT|DELETE|GET|HEAD|OPTIONS|PATCH|POST|PRI|PUT|SEARCH|TRACE)\s(?:https?:\/\/|\/)\S*\sHTTP\/[\d.]+/m, @@ -94,66 +149,9 @@ export default { 'punctuation': /^:/, }, }, + $insertBefore: { + 'header': options, + }, }; - - // Create a mapping of Content-Type headers to language definitions - const httpLanguages = { - 'application/javascript': 'javascript', - 'application/json': getOptionalLanguage('json') || 'javascript', - 'application/xml': 'xml', - 'text/xml': 'xml', - 'text/html': 'html', - 'text/css': 'css', - 'text/plain': 'plain', - }; - - // Declare which types can also be suffixes - const suffixTypes = { - 'application/json': true, - 'application/xml': true, - }; - - /** - * Returns a pattern for the given content type which matches it and any type which has it as a suffix. - * - * @param {string} contentType - * @returns {string} - */ - function getSuffixPattern (contentType) { - const suffix = contentType.replace(/^[a-z]+\//, ''); - const suffixPattern = '\\w+/(?:[\\w.-]+\\+)+' + suffix + '(?![+\\w.-])'; - return '(?:' + contentType + '|' + suffixPattern + ')'; - } - - // Insert each content type parser that has its associated language - // currently loaded. - /** @type {import('../types.d.ts').Grammar} */ - const options = {}; - for (const key in httpLanguages) { - const contentType = key; - - const pattern = suffixTypes[contentType] ? getSuffixPattern(contentType) : contentType; - options[contentType.replace(/\//g, '-')] = { - pattern: RegExp( - '(' + - /content-type:\s*/.source + - pattern + - /(?:(?:\r\n?|\n)[\w-].*)*(?:\r(?:\n|(?!\n))|\n)/.source + - ')' + - // This is a little interesting: - // The HTTP format spec required 1 empty line before the body to make everything unambiguous. - // However, when writing code by hand (e.g. to display on a website) people can forget about this, - // so we want to be liberal here. We will allow the empty line to be omitted if the first line of - // the body does not start with a [\w-] character (as headers do). - /[^ \t\w-][\s\S]*/.source, - 'i' - ), - lookbehind: true, - inside: httpLanguages[contentType], - }; - } - insertBefore(http, 'header', options); - - return http; }, }; diff --git a/src/languages/icu-message-format.js b/src/languages/icu-message-format.js index efa852473f..413f3c0848 100644 --- a/src/languages/icu-message-format.js +++ b/src/languages/icu-message-format.js @@ -58,7 +58,7 @@ export default { }, }; - return /** @type {Grammar} */ ({ + return { 'argument': { pattern: RegExp(argumentSource), greedy: true, @@ -85,7 +85,7 @@ export default { 'number': /\S+/, }, }, - $rest: /** @type {Grammar['$rest']} */ ('icu-message-format'), + $rest: 'icu-message-format', }, }, 'plural-style': { @@ -152,10 +152,6 @@ export default { }, 'escape': escape, 'string': string, - }); + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/idris.js b/src/languages/idris.js index f3f8b61752..cb17f68194 100644 --- a/src/languages/idris.js +++ b/src/languages/idris.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import haskell from './haskell.js'; /** @type {import('../types.d.ts').LanguageProto<'idris'>} */ @@ -6,17 +5,7 @@ export default { id: 'idris', base: haskell, alias: 'idr', - grammar ({ base }) { - insertBefore(base, 'keyword', { - 'import-statement': { - pattern: /(^\s*import\s+)(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*/m, - lookbehind: true, - inside: { - 'punctuation': /\./, - }, - }, - }); - + grammar () { return { 'comment': { pattern: /(?:(?:--|\|\|\|).*$|\{-[\s\S]*?-\})/m, @@ -24,6 +13,16 @@ export default { 'keyword': /\b(?:Type|case|class|codata|constructor|corecord|data|do|dsl|else|export|if|implementation|implicit|import|impossible|in|infix|infixl|infixr|instance|interface|let|module|mutual|namespace|of|parameters|partial|postulate|private|proof|public|quoteGoal|record|rewrite|syntax|then|total|using|where|with)\b/, 'builtin': undefined, + $insert: { + 'import-statement': { + $before: 'keyword', + pattern: /(^\s*import\s+)(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*/m, + lookbehind: true, + inside: { + 'punctuation': /\./, + }, + }, + }, }; }, }; diff --git a/src/languages/inform7.js b/src/languages/inform7.js index e6a772faa0..eefea4e960 100644 --- a/src/languages/inform7.js +++ b/src/languages/inform7.js @@ -71,7 +71,7 @@ export default { }, }); - return /** @type {Grammar} */ (inform7); + return inform7; }, }; diff --git a/src/languages/java.js b/src/languages/java.js index 197eddc50a..7642db53d9 100644 --- a/src/languages/java.js +++ b/src/languages/java.js @@ -1,5 +1,4 @@ import { toArray } from '../util/iterables.js'; -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'java'>} */ @@ -30,88 +29,6 @@ export default { }, }; - insertBefore(base, 'comment', { - 'doc-comment': { - pattern: /\/\*\*(?!\/)[\s\S]*?(?:\*\/|$)/, - greedy: true, - alias: 'comment', - inside: 'javadoc', - }, - }); - - insertBefore(base, 'string', { - 'triple-quoted-string': { - // http://openjdk.java.net/jeps/355#Description - pattern: /"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/, - greedy: true, - alias: 'string', - }, - 'char': { - pattern: /'(?:\\.|[^'\\\r\n]){1,6}'/, - greedy: true, - }, - }); - - insertBefore(base, 'class-name', { - 'annotation': { - pattern: /(^|[^.])@\w+(?:\s*\.\s*\w+)*/, - lookbehind: true, - alias: 'punctuation', - }, - 'generics': { - pattern: - /<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/, - inside: { - 'class-name': className, - 'keyword': keywords, - 'punctuation': /[<>(),.:]/, - 'operator': /[?&|]/, - }, - }, - 'import': [ - { - pattern: RegExp( - /(\bimport\s+)/.source + classNamePrefix + /(?:[A-Z]\w*|\*)(?=\s*;)/.source - ), - lookbehind: true, - inside: { - 'namespace': className.inside.namespace, - 'punctuation': /\./, - 'operator': /\*/, - 'class-name': /\w+/, - }, - }, - { - pattern: RegExp( - /(\bimport\s+static\s+)/.source + - classNamePrefix + - /(?:\w+|\*)(?=\s*;)/.source - ), - lookbehind: true, - alias: 'static', - inside: { - 'namespace': className.inside.namespace, - 'static': /\b\w+$/, - 'punctuation': /\./, - 'operator': /\*/, - 'class-name': /\w+/, - }, - }, - ], - 'namespace': { - pattern: RegExp( - /(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace( - //g, - () => keywords.source - ) - ), - lookbehind: true, - inside: { - 'punctuation': /\./, - }, - }, - }); - return { 'string': { pattern: /(^|[^\\])"(?:\\.|[^"\\\r\n])*"/, @@ -146,7 +63,7 @@ export default { ], 'keyword': keywords, 'function': [ - ...toArray(base.function), + ...toArray(/** @type {import('../types.d.ts').GrammarTokens} */ (base).function), { pattern: /(::\s*)[a-z_]\w*/, lookbehind: true, @@ -159,6 +76,89 @@ export default { lookbehind: true, }, 'constant': /\b[A-Z][A-Z_\d]+\b/, + $insertBefore: { + 'comment': { + 'doc-comment': { + pattern: /\/\*\*(?!\/)[\s\S]*?(?:\*\/|$)/, + greedy: true, + alias: 'comment', + inside: 'javadoc', + }, + }, + 'string': { + 'triple-quoted-string': { + // http://openjdk.java.net/jeps/355#Description + pattern: /"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/, + greedy: true, + alias: 'string', + }, + 'char': { + pattern: /'(?:\\.|[^'\\\r\n]){1,6}'/, + greedy: true, + }, + }, + 'class-name': { + 'annotation': { + pattern: /(^|[^.])@\w+(?:\s*\.\s*\w+)*/, + lookbehind: true, + alias: 'punctuation', + }, + 'generics': { + pattern: + /<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/, + inside: { + 'class-name': className, + 'keyword': keywords, + 'punctuation': /[<>(),.:]/, + 'operator': /[?&|]/, + }, + }, + 'import': [ + { + pattern: RegExp( + /(\bimport\s+)/.source + + classNamePrefix + + /(?:[A-Z]\w*|\*)(?=\s*;)/.source + ), + lookbehind: true, + inside: { + 'namespace': className.inside.namespace, + 'punctuation': /\./, + 'operator': /\*/, + 'class-name': /\w+/, + }, + }, + { + pattern: RegExp( + /(\bimport\s+static\s+)/.source + + classNamePrefix + + /(?:\w+|\*)(?=\s*;)/.source + ), + lookbehind: true, + alias: 'static', + inside: { + 'namespace': className.inside.namespace, + 'static': /\b\w+$/, + 'punctuation': /\./, + 'operator': /\*/, + 'class-name': /\w+/, + }, + }, + ], + 'namespace': { + pattern: RegExp( + /(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace( + //g, + () => keywords.source + ) + ), + lookbehind: true, + inside: { + 'punctuation': /\./, + }, + }, + }, + }, }; }, }; diff --git a/src/languages/javadoc.js b/src/languages/javadoc.js index 5963501c2c..2aa413e789 100644 --- a/src/languages/javadoc.js +++ b/src/languages/javadoc.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import java from './java.js'; import javadoclike from './javadoclike.js'; import markup from './markup.js'; @@ -8,7 +7,7 @@ export default { id: 'javadoc', base: javadoclike, require: [markup, java], - grammar ({ base, languages }) { + grammar ({ languages }) { const { tag, entity } = languages.markup; const codeLinePattern = /(^(?:[\t ]*(?:\*\s*)*))[^*\s].*$/m; @@ -19,84 +18,86 @@ export default { () => memberReference ); - insertBefore(base, 'keyword', { - 'reference': { - pattern: RegExp( - /(@(?:exception|link|linkplain|see|throws|value)\s+(?:\*\s*)?)/.source + - '(?:' + - reference + - ')' - ), - lookbehind: true, - inside: { - 'function': { - pattern: /(#\s*)\w+(?=\s*\()/, + return { + $insertBefore: { + 'keyword': { + 'reference': { + pattern: RegExp( + /(@(?:exception|link|linkplain|see|throws|value)\s+(?:\*\s*)?)/.source + + '(?:' + + reference + + ')' + ), lookbehind: true, - }, - 'field': { - pattern: /(#\s*)\w+/, - lookbehind: true, - }, - 'namespace': { - pattern: /\b(?:[a-z]\w*\s*\.\s*)+/, inside: { - 'punctuation': /\./, + 'function': { + pattern: /(#\s*)\w+(?=\s*\()/, + lookbehind: true, + }, + 'field': { + pattern: /(#\s*)\w+/, + lookbehind: true, + }, + 'namespace': { + pattern: /\b(?:[a-z]\w*\s*\.\s*)+/, + inside: { + 'punctuation': /\./, + }, + }, + 'class-name': /\b[A-Z]\w*/, + 'keyword': languages.java.keyword, + 'punctuation': /[#()[\],.]/, }, }, - 'class-name': /\b[A-Z]\w*/, - 'keyword': languages.java.keyword, - 'punctuation': /[#()[\],.]/, - }, - }, - 'class-name': { - // @param the first generic type parameter - pattern: /(@param\s+)<[A-Z]\w*>/, - lookbehind: true, - inside: { - 'punctuation': /[.<>]/, - }, - }, - 'code-section': [ - { - pattern: - /(\{@code\s+(?!\s))(?:[^\s{}]|\s+(?![\s}])|\{(?:[^{}]|\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*\})+(?=\s*\})/, - lookbehind: true, - inside: { - 'code': { - // there can't be any HTML inside of {@code} tags - pattern: codeLinePattern, - lookbehind: true, - inside: 'java', - alias: 'language-java', + 'class-name': { + // @param the first generic type parameter + pattern: /(@param\s+)<[A-Z]\w*>/, + lookbehind: true, + inside: { + 'punctuation': /[.<>]/, }, }, - }, - { - pattern: /(<(code|pre|tt)>(?!)\s*)\S(?:\S|\s+\S)*?(?=\s*<\/\2>)/, - lookbehind: true, - inside: { - 'line': { - pattern: codeLinePattern, + 'code-section': [ + { + pattern: + /(\{@code\s+(?!\s))(?:[^\s{}]|\s+(?![\s}])|\{(?:[^{}]|\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*\})+(?=\s*\})/, lookbehind: true, inside: { - // highlight HTML tags and entities - 'tag': tag, - 'entity': entity, 'code': { - // everything else is Java code - pattern: /.+/, + // there can't be any HTML inside of {@code} tags + pattern: codeLinePattern, + lookbehind: true, inside: 'java', alias: 'language-java', }, }, }, - }, + { + pattern: /(<(code|pre|tt)>(?!)\s*)\S(?:\S|\s+\S)*?(?=\s*<\/\2>)/, + lookbehind: true, + inside: { + 'line': { + pattern: codeLinePattern, + lookbehind: true, + inside: { + // highlight HTML tags and entities + 'tag': tag, + 'entity': entity, + 'code': { + // everything else is Java code + pattern: /.+/, + inside: 'java', + alias: 'language-java', + }, + }, + }, + }, + }, + ], + 'tag': tag, + 'entity': entity, }, - ], - 'tag': tag, - 'entity': entity, - }); - - return {}; + }, + }; }, }; diff --git a/src/languages/javascript.js b/src/languages/javascript.js index 645c9692b7..737edae8b6 100644 --- a/src/languages/javascript.js +++ b/src/languages/javascript.js @@ -1,6 +1,5 @@ import { JS_TEMPLATE, JS_TEMPLATE_INTERPOLATION } from '../shared/languages/patterns.js'; import { toArray } from '../util/iterables.js'; -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'javascript'>} */ @@ -9,135 +8,10 @@ export default { base: clike, optional: 'js-templates', alias: 'js', - grammar ({ base, getOptionalLanguage }) { - insertBefore(base, 'comment', { - 'doc-comment': { - pattern: /\/\*\*(?!\/)[\s\S]*?(?:\*\/|$)/, - greedy: true, - inside: 'jsdoc', - }, - }); - - insertBefore(base, 'keyword', { - 'regex': { - pattern: RegExp( - // lookbehind - // eslint-disable-next-line regexp/no-dupe-characters-character-class - /((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source + - // Regex pattern: - // There are 2 regex patterns here. The RegExp set notation proposal added support for nested character - // classes if the `v` flag is present. Unfortunately, nested CCs are both context-free and incompatible - // with the only syntax, so we have to define 2 different regex patterns. - /\//.source + - '(?:' + - /(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source + - '|' + - // `v` flag syntax. This supports 3 levels of nested character classes. - /(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/ - .source + - ')' + - // lookahead - /(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source - ), - lookbehind: true, - greedy: true, - inside: { - 'regex-source': { - pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/, - lookbehind: true, - alias: 'language-regex', - inside: 'regex', - }, - 'regex-delimiter': /^\/|\/$/, - 'regex-flags': /^[a-z]+$/, - }, - }, - // This must be declared before keyword because we use "function" inside the look-forward - 'function-variable': { - pattern: - /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/, - alias: 'function', - }, - 'parameter': [ - { - pattern: - /(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/, - lookbehind: true, - inside: 'javascript', - }, - { - pattern: - /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i, - lookbehind: true, - inside: 'javascript', - }, - { - pattern: /(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/, - lookbehind: true, - inside: 'javascript', - }, - { - pattern: - /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/, - lookbehind: true, - inside: 'javascript', - }, - ], - 'constant': /\b[A-Z](?:[A-Z_]|\dx?)*\b/, - }); - - const jsTemplates = getOptionalLanguage('js-templates')?.['template-string']; - - insertBefore(base, 'string', { - 'hashbang': { - pattern: /^#!.*/, - greedy: true, - alias: 'comment', - }, - 'template-string': [ - ...toArray(jsTemplates), - { - pattern: JS_TEMPLATE, - greedy: true, - inside: /** @type {Grammar} */ ({ - 'template-punctuation': { - pattern: /^`|`$/, - alias: 'string', - }, - 'interpolation': { - pattern: RegExp( - /((?:^|[^\\])(?:\\{2})*)/.source + JS_TEMPLATE_INTERPOLATION.source - ), - lookbehind: true, - inside: { - 'interpolation-punctuation': { - pattern: /^\$\{|\}$/, - alias: 'punctuation', - }, - $rest: /** @type {Grammar['$rest']} */ ('javascript'), - }, - }, - 'string': /[\s\S]+/, - }), - }, - ], - 'string-property': { - pattern: - /((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m, - lookbehind: true, - greedy: true, - alias: 'property', - }, - }); - - insertBefore(base, 'operator', { - 'literal-property': { - pattern: - /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m, - lookbehind: true, - alias: 'property', - }, - }); + grammar ({ getOptionalLanguage }) { + const jsTemplates = /** @type {GrammarTokens} */ (getOptionalLanguage('js-templates'))?.[ + 'template-string' + ]; return { 'class-name': [ @@ -210,10 +84,140 @@ export default { }, 'operator': /--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/, + $insertBefore: { + 'comment': { + 'doc-comment': { + pattern: /\/\*\*(?!\/)[\s\S]*?(?:\*\/|$)/, + greedy: true, + inside: 'jsdoc', + }, + }, + 'keyword': { + 'regex': { + pattern: RegExp( + // lookbehind + // eslint-disable-next-line regexp/no-dupe-characters-character-class + /((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source + + // Regex pattern: + // There are 2 regex patterns here. The RegExp set notation proposal added support for nested character + // classes if the `v` flag is present. Unfortunately, nested CCs are both context-free and incompatible + // with the only syntax, so we have to define 2 different regex patterns. + /\//.source + + '(?:' + + /(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/ + .source + + '|' + + // `v` flag syntax. This supports 3 levels of nested character classes. + /(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/ + .source + + ')' + + // lookahead + /(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/ + .source + ), + lookbehind: true, + greedy: true, + inside: { + 'regex-source': { + pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/, + lookbehind: true, + alias: 'language-regex', + inside: 'regex', + }, + 'regex-delimiter': /^\/|\/$/, + 'regex-flags': /^[a-z]+$/, + }, + }, + // This must be declared before keyword because we use "function" inside the look-forward + 'function-variable': { + pattern: + /#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/, + alias: 'function', + }, + 'parameter': [ + { + pattern: + /(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/, + lookbehind: true, + inside: 'javascript', + }, + { + pattern: + /(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i, + lookbehind: true, + inside: 'javascript', + }, + { + pattern: + /(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/, + lookbehind: true, + inside: 'javascript', + }, + { + pattern: + /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/, + lookbehind: true, + inside: 'javascript', + }, + ], + 'constant': /\b[A-Z](?:[A-Z_]|\dx?)*\b/, + }, + 'string': { + 'hashbang': { + pattern: /^#!.*/, + greedy: true, + alias: 'comment', + }, + 'template-string': [ + ...toArray(jsTemplates), + { + pattern: JS_TEMPLATE, + greedy: true, + inside: /** @type {Grammar} */ ({ + 'template-punctuation': { + pattern: /^`|`$/, + alias: 'string', + }, + 'interpolation': { + pattern: RegExp( + /((?:^|[^\\])(?:\\{2})*)/.source + + JS_TEMPLATE_INTERPOLATION.source + ), + lookbehind: true, + inside: { + 'interpolation-punctuation': { + pattern: /^\$\{|\}$/, + alias: 'punctuation', + }, + $rest: /** @type {Grammar['$rest']} */ ('javascript'), + }, + }, + 'string': /[\s\S]+/, + }), + }, + ], + 'string-property': { + pattern: + /((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m, + lookbehind: true, + greedy: true, + alias: 'property', + }, + }, + 'operator': { + 'literal-property': { + pattern: + /((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m, + lookbehind: true, + alias: 'property', + }, + }, + }, }; }, }; /** * @typedef {import('../types.d.ts').Grammar} Grammar + * @typedef {import('../types.d.ts').GrammarTokens} GrammarTokens */ diff --git a/src/languages/jolie.js b/src/languages/jolie.js index 73ad9b3af6..4849455d85 100644 --- a/src/languages/jolie.js +++ b/src/languages/jolie.js @@ -1,37 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'jolie'>} */ export default { id: 'jolie', base: clike, - grammar ({ base }) { - insertBefore(base, 'keyword', { - 'aggregates': { - pattern: - /(\bAggregates\s*:\s*)(?:\w+(?:\s+with\s+\w+)?\s*,\s*)*\w+(?:\s+with\s+\w+)?/, - lookbehind: true, - inside: { - 'keyword': /\bwith\b/, - 'class-name': /\w+/, - 'punctuation': /,/, - }, - }, - 'redirects': { - pattern: /(\bRedirects\s*:\s*)(?:\w+\s*=>\s*\w+\s*,\s*)*(?:\w+\s*=>\s*\w+)/, - lookbehind: true, - inside: { - 'punctuation': /,/, - 'class-name': /\w+/, - 'operator': /=>/, - }, - }, - 'property': { - pattern: - /\b(?:Aggregates|[Ii]nterfaces|Java|Javascript|Jolie|[Ll]ocation|OneWay|[Pp]rotocol|Redirects|RequestResponse)\b(?=[ \t]*:)/, - }, - }); - + grammar () { return { 'string': { pattern: /(^|[^\\])"(?:\\[\s\S]|[^"\\])*"/, @@ -51,6 +24,33 @@ export default { 'punctuation': /[()[\]{},;.:]/, 'builtin': /\b(?:Byte|any|bool|char|double|enum|float|int|length|long|ranges|regex|string|undefined|void)\b/, + $insertBefore: { + 'keyword': { + 'aggregates': { + pattern: + /(\bAggregates\s*:\s*)(?:\w+(?:\s+with\s+\w+)?\s*,\s*)*\w+(?:\s+with\s+\w+)?/, + lookbehind: true, + inside: { + 'keyword': /\bwith\b/, + 'class-name': /\w+/, + 'punctuation': /,/, + }, + }, + 'redirects': { + pattern: /(\bRedirects\s*:\s*)(?:\w+\s*=>\s*\w+\s*,\s*)*(?:\w+\s*=>\s*\w+)/, + lookbehind: true, + inside: { + 'punctuation': /,/, + 'class-name': /\w+/, + 'operator': /=>/, + }, + }, + 'property': { + pattern: + /\b(?:Aggregates|[Ii]nterfaces|Java|Javascript|Jolie|[Ll]ocation|OneWay|[Pp]rotocol|Redirects|RequestResponse)\b(?=[ \t]*:)/, + }, + }, + }, }; }, }; diff --git a/src/languages/js-templates.js b/src/languages/js-templates.js index 0693122835..a171a072f7 100644 --- a/src/languages/js-templates.js +++ b/src/languages/js-templates.js @@ -18,7 +18,7 @@ function createTemplate (language, tag) { lookbehind: true, greedy: true, alias: 'template-string', - inside: /** @type {Grammar} */ ({ + inside: { 'template-punctuation': { pattern: /^`|`$/, alias: 'string', @@ -42,7 +42,7 @@ function createTemplate (language, tag) { $tokenize: embeddedIn(language), }, }, - }), + }, }; } @@ -83,6 +83,5 @@ export default { }; /** - * @typedef {import('../types.d.ts').Grammar} Grammar * @typedef {import('../types.d.ts').GrammarToken} GrammarToken */ diff --git a/src/languages/jsdoc.js b/src/languages/jsdoc.js index 5c302c61f9..e251128a55 100644 --- a/src/languages/jsdoc.js +++ b/src/languages/jsdoc.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import javadoclike from './javadoclike.js'; import javascript from './javascript.js'; import typescript from './typescript.js'; @@ -8,85 +7,88 @@ export default { id: 'jsdoc', base: javadoclike, require: [javascript, typescript], - grammar ({ base, languages }) { + grammar ({ languages }) { const { javascript, typescript } = languages; const type = /\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})+\}/.source; const parameterPrefix = '(@(?:arg|argument|param|property)\\s+(?:' + type + '\\s+)?)'; - insertBefore(base, 'keyword', { - 'optional-parameter': { - // @param {string} [baz.foo="bar"] foo bar - pattern: RegExp( - parameterPrefix + /\[(?:(?!\s)[$\w\xA0-\uFFFF.])+(?:=[^[\]]+)?\](?=\s|$)/.source - ), + return { + 'parameter': { + // @param {string} foo - foo bar + pattern: RegExp(parameterPrefix + /(?:(?!\s)[$\w\xA0-\uFFFF.])+(?=\s|$)/.source), lookbehind: true, inside: { - 'parameter': { - pattern: /(^\[)[$\w\xA0-\uFFFF\.]+/, + 'punctuation': /\./, + }, + }, + $insertBefore: { + 'keyword': { + 'optional-parameter': { + // @param {string} [baz.foo="bar"] foo bar + pattern: RegExp( + parameterPrefix + + /\[(?:(?!\s)[$\w\xA0-\uFFFF.])+(?:=[^[\]]+)?\](?=\s|$)/.source + ), lookbehind: true, inside: { - 'punctuation': /\./, + 'parameter': { + pattern: /(^\[)[$\w\xA0-\uFFFF\.]+/, + lookbehind: true, + inside: { + 'punctuation': /\./, + }, + }, + 'code': { + pattern: /(=)[\s\S]*(?=\]$)/, + lookbehind: true, + alias: 'language-javascript', + inside: 'javascript', + }, + 'punctuation': /[=[\]]/, }, }, - 'code': { - pattern: /(=)[\s\S]*(?=\]$)/, - lookbehind: true, - alias: 'language-javascript', - inside: 'javascript', - }, - 'punctuation': /[=[\]]/, - }, - }, - 'class-name': [ - { - pattern: RegExp( - /(@(?:augments|class|extends|interface|memberof!?|template|this|typedef)\s+(?:\s+)?)[A-Z]\w*(?:\.[A-Z]\w*)*/.source.replace( - //g, - () => type - ) - ), - lookbehind: true, - inside: { - 'punctuation': /\./, - }, - }, - { - pattern: RegExp('(@[a-z]+\\s+)' + type), - lookbehind: true, - inside: { - 'string': javascript.string, - 'number': javascript.number, - 'boolean': javascript.boolean, - 'keyword': typescript.keyword, - 'operator': /=>|\.\.\.|[&|?:*]/, - 'punctuation': /[.,;=<>{}()[\]]/, - }, - }, - ], - 'example': { - pattern: /(@example\s+(?!\s))(?:[^@\s]|\s+(?!\s))+?(?=\s*(?:\*\s*)?(?:@\w|\*\/))/, - lookbehind: true, - inside: { - 'code': { - pattern: /^([\t ]*(?:\*\s*)?)\S.*$/m, + 'class-name': [ + { + pattern: RegExp( + /(@(?:augments|class|extends|interface|memberof!?|template|this|typedef)\s+(?:\s+)?)[A-Z]\w*(?:\.[A-Z]\w*)*/.source.replace( + //g, + () => type + ) + ), + lookbehind: true, + inside: { + 'punctuation': /\./, + }, + }, + { + pattern: RegExp('(@[a-z]+\\s+)' + type), + lookbehind: true, + inside: { + 'string': javascript.string, + 'number': javascript.number, + 'boolean': javascript.boolean, + 'keyword': typescript.keyword, + 'operator': /=>|\.\.\.|[&|?:*]/, + 'punctuation': /[.,;=<>{}()[\]]/, + }, + }, + ], + 'example': { + pattern: + /(@example\s+(?!\s))(?:[^@\s]|\s+(?!\s))+?(?=\s*(?:\*\s*)?(?:@\w|\*\/))/, lookbehind: true, - alias: 'language-javascript', - inside: 'javascript', + inside: { + 'code': { + pattern: /^([\t ]*(?:\*\s*)?)\S.*$/m, + lookbehind: true, + alias: 'language-javascript', + inside: 'javascript', + }, + }, }, }, }, - }); - - return { - 'parameter': { - // @param {string} foo - foo bar - pattern: RegExp(parameterPrefix + /(?:(?!\s)[$\w\xA0-\uFFFF.])+(?=\s|$)/.source), - lookbehind: true, - inside: { - 'punctuation': /\./, - }, - }, }; }, }; diff --git a/src/languages/jsonp.js b/src/languages/jsonp.js index daff133f6d..86fe9e8cfc 100644 --- a/src/languages/jsonp.js +++ b/src/languages/jsonp.js @@ -1,17 +1,17 @@ -import { insertBefore } from '../util/language-util.js'; import json from './json.js'; /** @type {import('../types.d.ts').LanguageProto<'jsonp'>} */ export default { id: 'jsonp', base: json, - grammar ({ base }) { - insertBefore(base, 'punctuation', { - 'function': /(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*\()/, - }); - + grammar () { return { 'punctuation': /[{}[\]();,.]/, + $insertBefore: { + 'punctuation': { + 'function': /(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*\()/, + }, + }, }; }, }; diff --git a/src/languages/kotlin.js b/src/languages/kotlin.js index 2d919506d3..f621197460 100644 --- a/src/languages/kotlin.js +++ b/src/languages/kotlin.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'kotlin'>} */ @@ -6,9 +5,7 @@ export default { id: 'kotlin', base: clike, alias: ['kt', 'kts'], - grammar ({ base }) { - delete base['class-name']; - + grammar () { const interpolationInside = { 'interpolation-punctuation': { pattern: /^\$\{?|\}$/, @@ -20,56 +17,6 @@ export default { }, }; - insertBefore(base, 'string', { - // https://kotlinlang.org/spec/expressions.html#string-interpolation-expressions - 'string-literal': [ - { - pattern: /"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/, - alias: 'multiline', - inside: { - 'interpolation': { - pattern: /\$(?:[a-z_]\w*|\{[^{}]*\})/i, - inside: interpolationInside, - }, - 'string': /[\s\S]+/, - }, - }, - { - pattern: /"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/, - alias: 'singleline', - inside: { - 'interpolation': { - pattern: /((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i, - lookbehind: true, - inside: interpolationInside, - }, - 'string': /[\s\S]+/, - }, - }, - ], - 'char': { - // https://kotlinlang.org/spec/expressions.html#character-literals - pattern: /'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/, - greedy: true, - }, - }); - - delete base['string']; - - insertBefore(base, 'keyword', { - 'annotation': { - pattern: /\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/, - alias: 'builtin', - }, - }); - - insertBefore(base, 'function', { - 'label': { - pattern: /\b\w+@|@\w+\b/, - alias: 'symbol', - }, - }); - return { 'keyword': { // The lookbehind prevents wrong highlighting of e.g. kotlin.properties.get @@ -92,6 +39,54 @@ export default { /\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/, 'operator': /\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/, + $insertBefore: { + 'string': { + // https://kotlinlang.org/spec/expressions.html#string-interpolation-expressions + 'string-literal': [ + { + pattern: /"""(?:[^$]|\$(?:(?!\{)|\{[^{}]*\}))*?"""/, + alias: 'multiline', + inside: { + 'interpolation': { + pattern: /\$(?:[a-z_]\w*|\{[^{}]*\})/i, + inside: interpolationInside, + }, + 'string': /[\s\S]+/, + }, + }, + { + pattern: /"(?:[^"\\\r\n$]|\\.|\$(?:(?!\{)|\{[^{}]*\}))*"/, + alias: 'singleline', + inside: { + 'interpolation': { + pattern: /((?:^|[^\\])(?:\\{2})*)\$(?:[a-z_]\w*|\{[^{}]*\})/i, + lookbehind: true, + inside: interpolationInside, + }, + 'string': /[\s\S]+/, + }, + }, + ], + 'char': { + // https://kotlinlang.org/spec/expressions.html#character-literals + pattern: /'(?:[^'\\\r\n]|\\(?:.|u[a-fA-F0-9]{0,4}))'/, + greedy: true, + }, + }, + 'keyword': { + 'annotation': { + pattern: /\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/, + alias: 'builtin', + }, + }, + 'function': { + 'label': { + pattern: /\b\w+@|@\w+\b/, + alias: 'symbol', + }, + }, + }, + $delete: ['class-name', 'string'], }; }, }; diff --git a/src/languages/latte.js b/src/languages/latte.js index 3965d597a2..4029e51fa5 100644 --- a/src/languages/latte.js +++ b/src/languages/latte.js @@ -38,7 +38,7 @@ export default { }, }); - return /** @type {Grammar} */ ({ + return { 'latte-comment': { pattern: /\{\*[\s\S]*?\*\}/, greedy: true, @@ -66,8 +66,8 @@ export default { }, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn(markupLatte)), - }); + $tokenize: embeddedIn(markupLatte), + }; }, }; diff --git a/src/languages/less.js b/src/languages/less.js index 9043b2e91f..2f98c1ff41 100644 --- a/src/languages/less.js +++ b/src/languages/less.js @@ -1,11 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import css from './css.js'; /** @type {import('../types.d.ts').LanguageProto<'less'>} */ export default { id: 'less', base: css, - grammar ({ base }) { + grammar () { /* FIXME : :extend() is not handled specifically : its highlighting is buggy. Mixin usage must be inside a ruleset to be highlighted. @@ -14,26 +13,6 @@ export default { A comment before a mixin usage prevents the latter to be properly highlighted. */ - insertBefore(base, 'property', { - 'variable': [ - // Variable declaration (the colon must be consumed!) - { - pattern: /@[\w-]+\s*:/, - inside: { - 'punctuation': /:/, - }, - }, - - // Variable usage - /@@?[\w-]+/, - ], - 'mixin-usage': { - pattern: /([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/, - lookbehind: true, - alias: 'function', - }, - }); - return { 'comment': [ /\/\*[\s\S]*?\*\//, @@ -60,6 +39,27 @@ export default { 'property': /(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/, 'operator': /[+\-*\/]/, + $insertBefore: { + 'property': { + 'variable': [ + // Variable declaration (the colon must be consumed!) + { + pattern: /@[\w-]+\s*:/, + inside: { + 'punctuation': /:/, + }, + }, + + // Variable usage + /@@?[\w-]+/, + ], + 'mixin-usage': { + pattern: /([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/, + lookbehind: true, + alias: 'function', + }, + }, + }, }; }, }; diff --git a/src/languages/lilypond.js b/src/languages/lilypond.js index d6f67b940b..000344d380 100644 --- a/src/languages/lilypond.js +++ b/src/languages/lilypond.js @@ -17,7 +17,7 @@ export default { } schemeExpression = schemeExpression.replace(//g, /[^\s\S]/.source); - return /** @type {Grammar} */ ({ + return { 'comment': /%(?:(?!\{).*|\{[\s\S]*?%\})/, 'embedded-scheme': { pattern: RegExp( @@ -47,7 +47,7 @@ export default { }, }, }, - $rest: /** @type {Grammar['$rest']} */ ('scheme'), + $rest: 'scheme', }, }, 'punctuation': /#/, @@ -74,10 +74,6 @@ export default { lookbehind: true, }, 'number': /\b\d+(?:\/\d+)?\b/, - }); + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/liquid.js b/src/languages/liquid.js index 42f23ef02b..7ceb6dd048 100644 --- a/src/languages/liquid.js +++ b/src/languages/liquid.js @@ -5,7 +5,7 @@ import markup from './markup.js'; export default { id: 'liquid', require: markup, - grammar: /** @type {Grammar} */ ({ + grammar: { 'ignore-raw': { pattern: /(\{%-?\s*raw\b[^\}]*\})[\s\S]*?(?=\{%-?\s*endraw\b[^\}]*\})/, lookbehind: true, @@ -59,10 +59,6 @@ export default { }, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }), + $tokenize: embeddedIn('markup'), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/lisp.js b/src/languages/lisp.js index 13ae53193f..edaa343993 100644 --- a/src/languages/lisp.js +++ b/src/languages/lisp.js @@ -52,7 +52,7 @@ export default { lookbehind: true, alias: 'variable', }, - $rest: /** @type {Grammar['$rest']} */ ('lisp'), + $rest: 'lisp', }; const forms = '\\S+(?:\\s+\\S+)*'; @@ -81,7 +81,7 @@ export default { }, }; - return /** @type {Grammar} */ ({ + return { // Three or four semicolons are considered a heading. // See https://www.gnu.org/software/emacs/manual/html_node/elisp/Comment-Tips.html 'heading': { @@ -207,10 +207,6 @@ export default { lookbehind: true, }, ], - }); + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/livescript.js b/src/languages/livescript.js index 4bbd1d839f..9809be3357 100644 --- a/src/languages/livescript.js +++ b/src/languages/livescript.js @@ -1,7 +1,7 @@ /** @type {import('../types.d.ts').LanguageProto<'livescript'>} */ export default { id: 'livescript', - grammar: /** @type {Grammar} */ ({ + grammar: { 'comment': [ { pattern: /(^|[^\\])\/\*[\s\S]*?\*\//, @@ -31,7 +31,7 @@ export default { pattern: /^#\{|\}$/, alias: 'variable', }, - $rest: /** @type {Grammar['$rest']} */ ('livescript'), + $rest: 'livescript', }, }, 'string': /[\s\S]+/, @@ -119,9 +119,5 @@ export default { /\.(?:[=~]|\.\.?)|\.(?:[&|^]|<<|>>>?)\.|:(?:=|:=?)|&&|\|[|>]|<(?:<[>=?]?|-(?:->?|>)?|\+\+?|@@?|%%?|\*\*?|!(?:~?=|--?>|~?~>)?|~(?:~?>|=)?|==?|\^\^?|[\/?]/, ], 'punctuation': /[(){}\[\]|.,:;`]/, - }), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/markdown.js b/src/languages/markdown.js index dca31fdcd9..77fac31a10 100644 --- a/src/languages/markdown.js +++ b/src/languages/markdown.js @@ -1,5 +1,5 @@ import { getTextContent } from '../core/classes/token.js'; -import { insertBefore, withoutTokenize } from '../util/language-util.js'; +import { withoutTokenize } from '../util/language-util.js'; import markup from './markup.js'; /** @type {import('../types.d.ts').LanguageProto<'markdown'>} */ @@ -7,7 +7,7 @@ export default { id: 'markdown', base: markup, alias: 'md', - grammar ({ base }) { + grammar () { // Allow only one line break const inner = /(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source; @@ -34,7 +34,7 @@ export default { const tableLine = /\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/ .source; - insertBefore(base, 'prolog', { + const markdown = { 'front-matter-block': { pattern: /(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/, lookbehind: true, @@ -321,23 +321,29 @@ export default { }, }, }, - }); + }; ['url', 'bold', 'italic', 'strike'].forEach(token => { ['url', 'bold', 'italic', 'strike', 'code-snippet'].forEach(inside => { - if (token !== inside) { - /** @type {Grammar} */ ( - /** @type {GrammarToken}*/ ( - /** @type {Grammar} */ ( - /** @type {GrammarToken} */ (base[token]).inside - ).content - ).inside - )[inside] = base[inside]; + if (token === inside) { + return; } + + /** @type {Grammar} */ ( + /** @type {GrammarToken}*/ ( + /** @type {Grammar} */ ( + /** @type {GrammarToken} */ (markdown[token]).inside + ).content + ).inside + )[inside] = markdown[inside]; }); }); - return {}; + return { + $insertBefore: { + 'prolog': markdown, + }, + }; }, effect (Prism) { return Prism.hooks.add('wrap', env => { diff --git a/src/languages/markup.js b/src/languages/markup.js index 2c6a3b73dd..3a9e31f725 100644 --- a/src/languages/markup.js +++ b/src/languages/markup.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import xml from './xml.js'; /** @@ -96,31 +95,30 @@ export default { id: 'markup', base: xml, alias: ['html', 'svg', 'mathml'], - grammar ({ base }) { - insertBefore(base, 'cdata', { - 'style': inlineEmbedded('style', 'css'), - 'script': inlineEmbedded('script', 'javascript'), - }); - - const tag = /** @type {GrammarToken & { inside: Grammar }} */ (base.tag); - insertBefore(tag.inside, 'attr-value', { - 'special-attr': [ - attributeEmbedded('style', 'css'), - // add attribute support for all DOM events. - // https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events - attributeEmbedded( - /on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/ - .source, - 'javascript' - ), - ], - }); - - return {}; + grammar () { + return { + $insertBefore: { + 'cdata': { + 'style': inlineEmbedded('style', 'css'), + 'script': inlineEmbedded('script', 'javascript'), + }, + 'tag/attr-value': { + 'special-attr': [ + attributeEmbedded('style', 'css'), + // add attribute support for all DOM events. + // https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events + attributeEmbedded( + /on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/ + .source, + 'javascript' + ), + ], + }, + }, + }; }, }; /** * @typedef {import('../types.d.ts').GrammarToken} GrammarToken - * @typedef {import('../types.d.ts').Grammar} Grammar */ diff --git a/src/languages/mongodb.js b/src/languages/mongodb.js index 1fb55d74ec..ab26a6cc44 100644 --- a/src/languages/mongodb.js +++ b/src/languages/mongodb.js @@ -1,11 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import javascript from './javascript.js'; /** @type {import('../types.d.ts').LanguageProto<'mongodb'>} */ export default { id: 'mongodb', base: javascript, - grammar ({ base }) { + grammar () { let operators = [ // query and projection '$eq', @@ -276,39 +275,41 @@ export default { const operatorsSource = '(?:' + operators.join('|') + ')\\b'; - insertBefore(base, 'string', { - 'property': { - pattern: - /(?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)(?=\s*:)/, - greedy: true, - inside: { - 'keyword': RegExp('^([\'"])?' + operatorsSource + '(?:\\1)?$'), + return { + $insert: { + 'property': { + $before: 'string', + pattern: + /(?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)(?=\s*:)/, + greedy: true, + inside: { + 'keyword': RegExp('^([\'"])?' + operatorsSource + '(?:\\1)?$'), + }, + }, + 'builtin': { + $before: 'constant', + pattern: RegExp('\\b(?:' + builtinFunctions.join('|') + ')\\b'), + alias: 'keyword', }, }, - }); - - const string = /** @type {import('../types.d.ts').GrammarToken} */ (base['string']); - string.inside = { - url: { - // url pattern - pattern: /https?:\/\/[-\w@:%.+~#=]{1,256}\.[a-z0-9()]{1,6}\b[-\w()@:%+.~#?&/=]*/i, - greedy: true, - }, - entity: { - // ipv4 - pattern: - /\b(?:(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d\d?|2[0-4]\d|25[0-5])\b/, - greedy: true, + $merge: { + 'string': { + inside: { + url: { + // url pattern + pattern: + /https?:\/\/[-\w@:%.+~#=]{1,256}\.[a-z0-9()]{1,6}\b[-\w()@:%+.~#?&/=]*/i, + greedy: true, + }, + entity: { + // ipv4 + pattern: + /\b(?:(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d\d?|2[0-4]\d|25[0-5])\b/, + greedy: true, + }, + }, + }, }, }; - - insertBefore(base, 'constant', { - 'builtin': { - pattern: RegExp('\\b(?:' + builtinFunctions.join('|') + ')\\b'), - alias: 'keyword', - }, - }); - - return {}; }, }; diff --git a/src/languages/n4js.js b/src/languages/n4js.js index 587c17b021..acb1778aab 100644 --- a/src/languages/n4js.js +++ b/src/languages/n4js.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import javascript from './javascript.js'; /** @type {import('../types.d.ts').LanguageProto<'n4js'>} */ @@ -6,19 +5,19 @@ export default { id: 'n4js', base: javascript, alias: 'n4jsd', - grammar ({ base }) { - insertBefore(base, 'constant', { - // Annotations in N4JS spec: https://numberfour.github.io/n4js/spec/N4JSSpec.html#_annotations - 'annotation': { - pattern: /@+\w+/, - alias: 'operator', - }, - }); - + grammar () { return { // Keywords from N4JS language spec: https://numberfour.github.io/n4js/spec/N4JSSpec.html 'keyword': /\b(?:Array|any|boolean|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|module|new|null|number|package|private|protected|public|return|set|static|string|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/, + $insert: { + // Annotations in N4JS spec: https://numberfour.github.io/n4js/spec/N4JSSpec.html#_annotations + 'annotation': { + $before: 'constant', + pattern: /@+\w+/, + alias: 'operator', + }, + }, }; }, }; diff --git a/src/languages/naniscript.js b/src/languages/naniscript.js index 6e570c400c..1921c11fa7 100644 --- a/src/languages/naniscript.js +++ b/src/languages/naniscript.js @@ -58,7 +58,7 @@ export default { ], }; - return /** @type {Grammar} */ ({ + return { // ; ... 'comment': { pattern: /^([\t ]*);.*/m, @@ -138,7 +138,6 @@ export default { }, }, - /** @type {Grammar['$tokenize']} */ $tokenize (code, grammar, Prism) { const tokens = Prism.tokenize(code, withoutTokenize(grammar)); tokens.forEach(token => { @@ -152,10 +151,6 @@ export default { }); return tokens; }, - }); + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/objectivec.js b/src/languages/objectivec.js index e3ccf178c3..77f66f827c 100644 --- a/src/languages/objectivec.js +++ b/src/languages/objectivec.js @@ -5,9 +5,7 @@ export default { id: 'objectivec', base: c, alias: 'objc', - grammar ({ base }) { - delete base['class-name']; - + grammar () { return { 'string': { pattern: /@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/, @@ -16,6 +14,7 @@ export default { 'keyword': /\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/, 'operator': /-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/, + $delete: ['class-name'], }; }, }; diff --git a/src/languages/opencl.js b/src/languages/opencl.js index 89d177358a..ea549657df 100644 --- a/src/languages/opencl.js +++ b/src/languages/opencl.js @@ -1,22 +1,11 @@ -import { insertBefore } from '../util/language-util.js'; import c from './c.js'; /** @type {import('../types.d.ts').LanguageProto<'opencl'>} */ export default { id: 'opencl', base: c, - grammar ({ base }) { + grammar () { /* OpenCL kernel language */ - insertBefore(base, 'class-name', { - // https://www.khronos.org/registry/OpenCL/sdk/2.1/docs/man/xhtml/scalarDataTypes.html - // https://www.khronos.org/registry/OpenCL/sdk/2.1/docs/man/xhtml/otherDataTypes.html - 'builtin-type': { - pattern: - /\b(?:_cl_(?:command_queue|context|device_id|event|kernel|mem|platform_id|program|sampler)|cl_(?:image_format|mem_fence_flags)|clk_event_t|event_t|image(?:1d_(?:array_|buffer_)?t|2d_(?:array_(?:depth_|msaa_|msaa_depth_)?|depth_|msaa_|msaa_depth_)?t|3d_t)|intptr_t|ndrange_t|ptrdiff_t|queue_t|reserve_id_t|sampler_t|size_t|uintptr_t)\b/, - alias: 'keyword', - }, - }); - return { // Extracted from the official specs (2.0) and http://streamcomputing.eu/downloads/?opencl.lang (opencl-keywords, opencl-types) and http://sourceforge.net/tracker/?func=detail&aid=2957794&group_id=95717&atid=612384 (Words2, partly Words3) 'keyword': @@ -32,6 +21,16 @@ export default { /\b(?:CHAR_(?:BIT|MAX|MIN)|CLK_(?:ADDRESS_(?:CLAMP(?:_TO_EDGE)?|NONE|REPEAT)|FILTER_(?:LINEAR|NEAREST)|(?:GLOBAL|LOCAL)_MEM_FENCE|NORMALIZED_COORDS_(?:FALSE|TRUE))|CL_(?:A?R?G?B?[Ax]?|BGRA|(?:HALF_)?FLOAT|INTENSITY|LUMINANCE|(?:(?:UN)?SIGNED|[US]NORM)_(?:INT(?:8|16|32))|UNORM_(?:INT_101010|SHORT_(?:555|565)))|(?:DBL|FLT|HALF)_(?:DIG|EPSILON|MANT_DIG|(?:MAX|MIN)(?:(?:_10)?_EXP)?)|FLT_RADIX|HUGE_VALF?|INFINITY|(?:INT|LONG|SCHAR|SHRT)_(?:MAX|MIN)|MAXFLOAT|M_(?:[12]_PI|2_SQRTPI|E|LN(?:2|10)|LOG(?:2|10)E?|PI(?:_[24])?|SQRT(?:1_2|2))(?:_F|_H)?|NAN|(?:UCHAR|UINT|ULONG|USHRT)_MAX)\b/, alias: 'constant', }, + $insert: { + // https://www.khronos.org/registry/OpenCL/sdk/2.1/docs/man/xhtml/scalarDataTypes.html + // https://www.khronos.org/registry/OpenCL/sdk/2.1/docs/man/xhtml/otherDataTypes.html + 'builtin-type': { + $before: 'class-name', + pattern: + /\b(?:_cl_(?:command_queue|context|device_id|event|kernel|mem|platform_id|program|sampler)|cl_(?:image_format|mem_fence_flags)|clk_event_t|event_t|image(?:1d_(?:array_|buffer_)?t|2d_(?:array_(?:depth_|msaa_|msaa_depth_)?|depth_|msaa_|msaa_depth_)?t|3d_t)|intptr_t|ndrange_t|ptrdiff_t|queue_t|reserve_id_t|sampler_t|size_t|uintptr_t)\b/, + alias: 'keyword', + }, + }, }; }, }; diff --git a/src/languages/parser.js b/src/languages/parser.js index b6e2cad693..8adbff39d1 100644 --- a/src/languages/parser.js +++ b/src/languages/parser.js @@ -1,14 +1,20 @@ -import { insertBefore } from '../util/language-util.js'; import markup from './markup.js'; /** @type {import('../types.d.ts').LanguageProto<'parser'>} */ export default { id: 'parser', base: markup, - grammar ({ base }) { + grammar () { const punctuation = /[\[\](){};]/; const parser = { + 'expression': { + // Allow for 3 levels of depth + pattern: /(^|[^^])\((?:[^()]|\((?:[^()]|\((?:[^()])*\))*\))*\)/, + greedy: true, + lookbehind: true, + inside: {}, // See below + }, 'keyword': { pattern: /(^|[^^])(?:\^(?:case|eval|for|if|switch|throw)\b|@(?:BASE|CLASS|GET(?:_DEFAULT)?|OPTIONS|SET_DEFAULT|USE)\b)/, @@ -39,62 +45,44 @@ export default { 'punctuation': punctuation, }; - insertBefore(parser, 'keyword', { - 'parser-comment': { - pattern: /(\s)#.*/, + parser['expression'].inside = { + 'string': { + pattern: /(^|[^^])(["'])(?:(?!\2)[^^]|\^[\s\S])*\2/, lookbehind: true, - alias: 'comment', }, - 'expression': { - // Allow for 3 levels of depth - pattern: /(^|[^^])\((?:[^()]|\((?:[^()]|\((?:[^()])*\))*\))*\)/, - greedy: true, - lookbehind: true, - inside: { - 'string': { - pattern: /(^|[^^])(["'])(?:(?!\2)[^^]|\^[\s\S])*\2/, + 'keyword': parser.keyword, + 'variable': parser.variable, + 'function': parser.function, + 'boolean': /\b(?:false|true)\b/, + 'number': /\b(?:0x[a-f\d]+|\d+(?:\.\d*)?(?:e[+-]?\d+)?)\b/i, + 'escape': parser.escape, + 'operator': + /[~+*\/\\%]|!(?:\|\|?|=)?|&&?|\|\|?|==|<[<=]?|>[>=]?|-[fd]?|\b(?:def|eq|ge|gt|in|is|le|lt|ne)\b/, + 'punctuation': punctuation, + }; + + return { + ...parser, + $insertBefore: { + 'keyword': { + 'parser-comment': { + pattern: /(\s)#.*/, lookbehind: true, + alias: 'comment', }, + }, + 'tag/attr-value/punctuation': { + 'expression': parser.expression, 'keyword': parser.keyword, 'variable': parser.variable, 'function': parser.function, - 'boolean': /\b(?:false|true)\b/, - 'number': /\b(?:0x[a-f\d]+|\d+(?:\.\d*)?(?:e[+-]?\d+)?)\b/i, 'escape': parser.escape, - 'operator': - /[~+*\/\\%]|!(?:\|\|?|=)?|&&?|\|\|?|==|<[<=]?|>[>=]?|-[fd]?|\b(?:def|eq|ge|gt|in|is|le|lt|ne)\b/, - 'punctuation': punctuation, + 'parser-punctuation': { + pattern: punctuation, + alias: 'punctuation', + }, }, }, - }); - - insertBefore( - /** @type {Grammar} */ ( - /** @type {GrammarToken} */ ( - /** @type {Grammar} */ (/** @type {GrammarToken} */ (base['tag']).inside)[ - 'attr-value' - ] - ).inside - ), - 'punctuation', - { - 'expression': parser.expression, - 'keyword': parser.keyword, - 'variable': parser.variable, - 'function': parser.function, - 'escape': parser.escape, - 'parser-punctuation': { - pattern: punctuation, - alias: 'punctuation', - }, - } - ); - - return parser; + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - * @typedef {import('../types.d.ts').GrammarToken} GrammarToken - */ diff --git a/src/languages/phpdoc.js b/src/languages/phpdoc.js index 4864d3a7fd..a3937b04d8 100644 --- a/src/languages/phpdoc.js +++ b/src/languages/phpdoc.js @@ -1,16 +1,24 @@ -import { insertBefore } from '../util/language-util.js'; import javadoclike from './javadoclike.js'; /** @type {import('../types.d.ts').LanguageProto<'phpdoc'>} */ export default { id: 'phpdoc', base: javadoclike, - grammar ({ base }) { + grammar () { const typeExpression = /(?:\b[a-zA-Z]\w*|[|\\[\]])+/.source; - insertBefore(base, 'keyword', { - 'class-name': [ - { + return { + 'parameter': { + pattern: RegExp( + '(@(?:global|param|property(?:-read|-write)?|var)\\s+(?:' + + typeExpression + + '\\s+)?)\\$\\w+' + ), + lookbehind: true, + }, + $insert: { + 'class-name': { + $before: 'keyword', pattern: RegExp( '(@(?:global|package|param|property(?:-read|-write)?|return|subpackage|throws|var)\\s+)' + typeExpression @@ -22,17 +30,6 @@ export default { 'punctuation': /[|\\[\]()]/, }, }, - ], - }); - - return { - 'parameter': { - pattern: RegExp( - '(@(?:global|param|property(?:-read|-write)?|var)\\s+(?:' + - typeExpression + - '\\s+)?)\\$\\w+' - ), - lookbehind: true, }, }; }, diff --git a/src/languages/plsql.js b/src/languages/plsql.js index 963e90f189..d8e842bcbb 100644 --- a/src/languages/plsql.js +++ b/src/languages/plsql.js @@ -1,18 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import sql from './sql.js'; /** @type {import('../types.d.ts').LanguageProto<'plsql'>} */ export default { id: 'plsql', base: sql, - grammar ({ base }) { - insertBefore(base, 'operator', { - 'label': { - pattern: /<<\s*\w+\s*>>/, - alias: 'symbol', - }, - }); - + grammar () { return { 'comment': { pattern: /\/\*[\s\S]*?\*\/|--.*/, @@ -23,6 +15,13 @@ export default { /\b(?:A|ACCESSIBLE|ADD|AGENT|AGGREGATE|ALL|ALTER|AND|ANY|ARRAY|AS|ASC|AT|ATTRIBUTE|AUTHID|AVG|BEGIN|BETWEEN|BFILE_BASE|BINARY|BLOB_BASE|BLOCK|BODY|BOTH|BOUND|BULK|BY|BYTE|C|CALL|CALLING|CASCADE|CASE|CHAR|CHARACTER|CHARSET|CHARSETFORM|CHARSETID|CHAR_BASE|CHECK|CLOB_BASE|CLONE|CLOSE|CLUSTER|CLUSTERS|COLAUTH|COLLECT|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPILED|COMPRESS|CONNECT|CONSTANT|CONSTRUCTOR|CONTEXT|CONTINUE|CONVERT|COUNT|CRASH|CREATE|CREDENTIAL|CURRENT|CURSOR|CUSTOMDATUM|DANGLING|DATA|DATE|DATE_BASE|DAY|DECLARE|DEFAULT|DEFINE|DELETE|DESC|DETERMINISTIC|DIRECTORY|DISTINCT|DOUBLE|DROP|DURATION|ELEMENT|ELSE|ELSIF|EMPTY|END|ESCAPE|EXCEPT|EXCEPTION|EXCEPTIONS|EXCLUSIVE|EXECUTE|EXISTS|EXIT|EXTERNAL|FETCH|FINAL|FIRST|FIXED|FLOAT|FOR|FORALL|FORCE|FROM|FUNCTION|GENERAL|GOTO|GRANT|GROUP|HASH|HAVING|HEAP|HIDDEN|HOUR|IDENTIFIED|IF|IMMEDIATE|IMMUTABLE|IN|INCLUDING|INDEX|INDEXES|INDICATOR|INDICES|INFINITE|INSERT|INSTANTIABLE|INT|INTERFACE|INTERSECT|INTERVAL|INTO|INVALIDATE|IS|ISOLATION|JAVA|LANGUAGE|LARGE|LEADING|LENGTH|LEVEL|LIBRARY|LIKE|LIKE2|LIKE4|LIKEC|LIMIT|LIMITED|LOCAL|LOCK|LONG|LOOP|MAP|MAX|MAXLEN|MEMBER|MERGE|MIN|MINUS|MINUTE|MOD|MODE|MODIFY|MONTH|MULTISET|MUTABLE|NAME|NAN|NATIONAL|NATIVE|NCHAR|NEW|NOCOMPRESS|NOCOPY|NOT|NOWAIT|NULL|NUMBER_BASE|OBJECT|OCICOLL|OCIDATE|OCIDATETIME|OCIDURATION|OCIINTERVAL|OCILOBLOCATOR|OCINUMBER|OCIRAW|OCIREF|OCIREFCURSOR|OCIROWID|OCISTRING|OCITYPE|OF|OLD|ON|ONLY|OPAQUE|OPEN|OPERATOR|OPTION|OR|ORACLE|ORADATA|ORDER|ORGANIZATION|ORLANY|ORLVARY|OTHERS|OUT|OVERLAPS|OVERRIDING|PACKAGE|PARALLEL_ENABLE|PARAMETER|PARAMETERS|PARENT|PARTITION|PASCAL|PERSISTABLE|PIPE|PIPELINED|PLUGGABLE|POLYMORPHIC|PRAGMA|PRECISION|PRIOR|PRIVATE|PROCEDURE|PUBLIC|RAISE|RANGE|RAW|READ|RECORD|REF|REFERENCE|RELIES_ON|REM|REMAINDER|RENAME|RESOURCE|RESULT|RESULT_CACHE|RETURN|RETURNING|REVERSE|REVOKE|ROLLBACK|ROW|SAMPLE|SAVE|SAVEPOINT|SB1|SB2|SB4|SECOND|SEGMENT|SELECT|SELF|SEPARATE|SEQUENCE|SERIALIZABLE|SET|SHARE|SHORT|SIZE|SIZE_T|SOME|SPARSE|SQL|SQLCODE|SQLDATA|SQLNAME|SQLSTATE|STANDARD|START|STATIC|STDDEV|STORED|STRING|STRUCT|STYLE|SUBMULTISET|SUBPARTITION|SUBSTITUTABLE|SUBTYPE|SUM|SYNONYM|TABAUTH|TABLE|TDO|THE|THEN|TIME|TIMESTAMP|TIMEZONE_ABBR|TIMEZONE_HOUR|TIMEZONE_MINUTE|TIMEZONE_REGION|TO|TRAILING|TRANSACTION|TRANSACTIONAL|TRUSTED|TYPE|UB1|UB2|UB4|UNDER|UNION|UNIQUE|UNPLUG|UNSIGNED|UNTRUSTED|UPDATE|USE|USING|VALIST|VALUE|VALUES|VARIABLE|VARIANCE|VARRAY|VARYING|VIEW|VIEWS|VOID|WHEN|WHERE|WHILE|WITH|WORK|WRAPPED|WRITE|YEAR|ZONE)\b/i, // https://docs.oracle.com/en/database/oracle/oracle-database/21/lnpls/plsql-language-fundamentals.html#GUID-96A42F7C-7A71-4B90-8255-CA9C8BD9722E 'operator': /:=?|=>|[<>^~!]=|\.\.|\|\||\*\*|[-+*/%<>=@]/, + $insert: { + 'label': { + $before: 'operator', + pattern: /<<\s*\w+\s*>>/, + alias: 'symbol', + }, + }, }; }, }; diff --git a/src/languages/processing.js b/src/languages/processing.js index f91bfacab5..6f13403479 100644 --- a/src/languages/processing.js +++ b/src/languages/processing.js @@ -1,26 +1,26 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'processing'>} */ export default { id: 'processing', base: clike, - grammar ({ base }) { - insertBefore(base, 'number', { - // Special case: XML is a type - 'constant': /\b(?!XML\b)[A-Z][A-Z\d_]+\b/, - 'type': { - pattern: /\b(?:boolean|byte|char|color|double|float|int|[A-Z]\w*)\b/, - alias: 'class-name', - }, - }); - + grammar () { return { 'keyword': /\b(?:break|case|catch|class|continue|default|else|extends|final|for|if|implements|import|new|null|private|public|return|static|super|switch|this|try|void|while)\b/, // Spaces are allowed between function name and parenthesis 'function': /\b\w+(?=\s*\()/, 'operator': /<[<=]?|>[>=]?|&&?|\|\|?|[%?]|[!=+\-*\/]=?/, + $insertBefore: { + 'number': { + // Special case: XML is a type + 'constant': /\b(?!XML\b)[A-Z][A-Z\d_]+\b/, + 'type': { + pattern: /\b(?:boolean|byte|char|color|double|float|int|[A-Z]\w*)\b/, + alias: 'class-name', + }, + }, + }, }; }, }; diff --git a/src/languages/protobuf.js b/src/languages/protobuf.js index 83ba187ceb..2e0a79df54 100644 --- a/src/languages/protobuf.js +++ b/src/languages/protobuf.js @@ -1,37 +1,13 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'protobuf'>} */ export default { id: 'protobuf', base: clike, - grammar ({ base }) { + grammar () { const builtinTypes = /\b(?:bool|bytes|double|s?fixed(?:32|64)|float|[su]?int(?:32|64)|string)\b/; - insertBefore(base, 'operator', { - 'map': { - pattern: /\bmap<\s*[\w.]+\s*,\s*[\w.]+\s*>(?=\s+[a-z_]\w*\s*[=;])/i, - alias: 'class-name', - inside: { - 'punctuation': /[<>.,]/, - 'builtin': builtinTypes, - }, - }, - 'builtin': builtinTypes, - 'positional-class-name': { - pattern: /(?:\b|\B\.)[a-z_]\w*(?:\.[a-z_]\w*)*(?=\s+[a-z_]\w*\s*[=;])/i, - alias: 'class-name', - inside: { - 'punctuation': /\./, - }, - }, - 'annotation': { - pattern: /(\[\s*)[a-z_]\w*(?=\s*=)/i, - lookbehind: true, - }, - }); - return { 'class-name': [ { @@ -47,6 +23,30 @@ export default { 'keyword': /\b(?:enum|extend|extensions|import|message|oneof|option|optional|package|public|repeated|required|reserved|returns|rpc(?=\s+\w)|service|stream|syntax|to)\b(?!\s*=\s*\d)/, 'function': /\b[a-z_]\w*(?=\s*\()/i, + $insertBefore: { + 'operator': { + 'map': { + pattern: /\bmap<\s*[\w.]+\s*,\s*[\w.]+\s*>(?=\s+[a-z_]\w*\s*[=;])/i, + alias: 'class-name', + inside: { + 'punctuation': /[<>.,]/, + 'builtin': builtinTypes, + }, + }, + 'builtin': builtinTypes, + 'positional-class-name': { + pattern: /(?:\b|\B\.)[a-z_]\w*(?:\.[a-z_]\w*)*(?=\s+[a-z_]\w*\s*[=;])/i, + alias: 'class-name', + inside: { + 'punctuation': /\./, + }, + }, + 'annotation': { + pattern: /(\[\s*)[a-z_]\w*(?=\s*=)/i, + lookbehind: true, + }, + }, + }, }; }, }; diff --git a/src/languages/pug.js b/src/languages/pug.js index 7930764cfb..e3b18df208 100644 --- a/src/languages/pug.js +++ b/src/languages/pug.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import javascript from './javascript.js'; import markup from './markup.js'; @@ -15,7 +14,49 @@ export default { // - Add explicit support for plain text using | // - Add support for markup embedded in plain text - const pug = /** @type {Grammar} */ ({ + const filter_pattern = + /(^([\t ]*)):(?:(?:\r?\n|\r(?!\n))(?:\2[\t ].+|\s*?(?=\r?\n|\r)))+/.source; + + // Non exhaustive list of available filters and associated languages + const filters = [ + { filter: 'atpl', language: 'twig' }, + { filter: 'coffee', language: 'coffeescript' }, + 'ejs', + 'handlebars', + 'less', + 'livescript', + 'markdown', + { filter: 'sass', language: 'scss' }, + 'stylus', + ]; + + const all_filters = /** @type {import('../types.d.ts').GrammarTokens} */ ({}); + for (const filterItem of filters) { + const { filter, language } = + typeof filterItem === 'string' + ? { filter: filterItem, language: filterItem } + : filterItem; + all_filters['filter-' + filter] = { + pattern: RegExp( + filter_pattern.replace('', () => filter), + 'm' + ), + lookbehind: true, + inside: { + 'filter-name': { + pattern: /^:[\w-]+/, + alias: 'variable', + }, + 'text': { + pattern: /\S[\s\S]*/, + alias: [language, 'language-' + language], + inside: language, + }, + }, + }; + } + + return { // Multiline stuff should appear before the rest // This handles both single-line and multi-line comments @@ -77,7 +118,7 @@ export default { pattern: /^(?:case|default|else|if|unless|when|while)\b/, alias: 'keyword', }, - $rest: /** @type {Grammar['$rest']} */ ('javascript'), + $rest: 'javascript', }, }, 'keyword': { @@ -153,57 +194,9 @@ export default { }, ], 'punctuation': /[.\-!=|]+/, - }); - - const filter_pattern = - /(^([\t ]*)):(?:(?:\r?\n|\r(?!\n))(?:\2[\t ].+|\s*?(?=\r?\n|\r)))+/.source; - - // Non exhaustive list of available filters and associated languages - const filters = [ - { filter: 'atpl', language: 'twig' }, - { filter: 'coffee', language: 'coffeescript' }, - 'ejs', - 'handlebars', - 'less', - 'livescript', - 'markdown', - { filter: 'sass', language: 'scss' }, - 'stylus', - ]; - - const all_filters = /** @type {GrammarTokens} */ ({}); - for (const filterItem of filters) { - const { filter, language } = - typeof filterItem === 'string' - ? { filter: filterItem, language: filterItem } - : filterItem; - all_filters['filter-' + filter] = { - pattern: RegExp( - filter_pattern.replace('', () => filter), - 'm' - ), - lookbehind: true, - inside: { - 'filter-name': { - pattern: /^:[\w-]+/, - alias: 'variable', - }, - 'text': { - pattern: /\S[\s\S]*/, - alias: [language, 'language-' + language], - inside: language, - }, - }, - }; - } - - insertBefore(pug, 'filter', all_filters); - - return pug; + $insertBefore: { + 'filter': all_filters, + }, + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - * @typedef {import('../types.d.ts').GrammarTokens} GrammarTokens - */ diff --git a/src/languages/puppet.js b/src/languages/puppet.js index a3cdf74c58..6b570ff586 100644 --- a/src/languages/puppet.js +++ b/src/languages/puppet.js @@ -34,7 +34,7 @@ export default { }, ]; - return /** @type {Grammar} */ ({ + return { 'heredoc': [ // Matches the content of a quoted heredoc string (subject to interpolation) { @@ -140,10 +140,6 @@ export default { 'operator': /=[=~>]?|![=~]?|<(?:<\|?|[=~|-])?|>[>=]?|->?|~>|\|>?>?|[*\/%+?]|\b(?:and|in|or)\b/, 'punctuation': /[\[\]{}().,;]|:+/, - }); + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/pure.js b/src/languages/pure.js index 01523de091..22abf02bc0 100644 --- a/src/languages/pure.js +++ b/src/languages/pure.js @@ -1,12 +1,61 @@ -import { insertBefore } from '../util/language-util.js'; - /** @type {import('../types.d.ts').LanguageProto<'pure'>} */ export default { id: 'pure', grammar () { // https://agraef.github.io/pure-docs/pure.html#lexical-matters - const pure = /** @type {Grammar} */ ({ + const inlineLang = { + pattern: /%<[\s\S]+?%>/, + greedy: true, + inside: { + 'lang': { + pattern: /(^%< *)-\*-.+?-\*-/, + lookbehind: true, + alias: 'comment', + }, + 'delimiter': { + pattern: /^%<.*|%>$/, + alias: 'punctuation', + }, + // C is the default inline language + $rest: 'c', + }, + }; + + const inlineLanguages = ['c', { lang: 'c++', alias: 'cpp' }, 'fortran']; + const inlineLanguageRe = /%< *-\*- *\d* *-\*-[\s\S]+?%>/.source; + + const insertBeforeInlineLang = []; + inlineLanguages.forEach(item => { + let alias; + let lang; + if (typeof item === 'string') { + alias = lang = item; + } + else { + alias = item.alias; + lang = item.lang; + } + + insertBeforeInlineLang.push([ + ['inline-lang-' + alias], + { + pattern: RegExp( + inlineLanguageRe.replace( + '', + lang.replace(/([.+*?\/\\(){}\[\]])/g, '\\$1') + ), + 'i' + ), + inside: { + ...inlineLang.inside, + $rest: alias, + }, + }, + ]); + }); + + return { 'comment': [ { pattern: /(^|[^\\])\/\*[\s\S]*?\*\//, @@ -18,23 +67,7 @@ export default { }, /#!.+/, ], - 'inline-lang': { - pattern: /%<[\s\S]+?%>/, - greedy: true, - inside: { - 'lang': { - pattern: /(^%< *)-\*-.+?-\*-/, - lookbehind: true, - alias: 'comment', - }, - 'delimiter': { - pattern: /^%<.*|%>$/, - alias: 'punctuation', - }, - // C is the default inline language - $rest: /** @type {Grammar['$rest']} */ ('c'), - }, - }, + 'inline-lang': inlineLang, 'string': { pattern: /"(?:\\.|[^"\\\r\n])*"/, greedy: true, @@ -59,46 +92,9 @@ export default { /(?:[!"#$%&'*+,\-.\/:<=>?@\\^`|~\u00a1-\u00bf\u00d7-\u00f7\u20d0-\u2bff]|\b_+\b)+|\b(?:and|div|mod|not|or)\b/, // FIXME: How can we prevent | and , to be highlighted as operator when they are used alone? 'punctuation': /[(){}\[\];,|]/, - }); - - const inlineLanguages = ['c', { lang: 'c++', alias: 'cpp' }, 'fortran']; - const inlineLanguageRe = /%< *-\*- *\d* *-\*-[\s\S]+?%>/.source; - - inlineLanguages.forEach(item => { - let alias; - let lang; - if (typeof item === 'string') { - alias = lang = item; - } - else { - alias = item.alias; - lang = item.lang; - } - - insertBefore(pure, 'inline-lang', { - ['inline-lang-' + alias]: { - pattern: RegExp( - inlineLanguageRe.replace( - '', - lang.replace(/([.+*?\/\\(){}\[\]])/g, '\\$1') - ), - 'i' - ), - inside: /** @type {Grammar} */ ({ - .../** @type {Grammar} */ ( - /** @type {GrammarToken} */ (pure['inline-lang']).inside - ), - $rest: alias, - }), - }, - }); - }); - - return pure; + $insertBefore: { + 'inline-lang': Object.fromEntries(insertBeforeInlineLang), + }, + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - * @typedef {import('../types.d.ts').GrammarToken} GrammarToken - */ diff --git a/src/languages/purebasic.js b/src/languages/purebasic.js index 24d59cfaa0..a73c0b8741 100644 --- a/src/languages/purebasic.js +++ b/src/languages/purebasic.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'purebasic'>} */ @@ -6,7 +5,7 @@ export default { id: 'purebasic', base: clike, alias: 'pbfasm', - grammar ({ base }) { + grammar () { /* Original Code by Bas Groothedde !!MANY THANKS!! I never would have made this, regex and me will never be best friends ;) @@ -14,59 +13,6 @@ export default { slightly changed to pass all tests */ - // PureBasic support, steal stuff from ansi-c - - insertBefore(base, 'keyword', { - 'tag': /#\w+\$?/, - 'asm': { - pattern: /(^[\t ]*)!.*/m, - lookbehind: true, - alias: 'tag', - inside: { - 'comment': /;.*/, - 'string': { - pattern: /(["'`])(?:\\.|(?!\1)[^\\\r\n])*\1/, - greedy: true, - }, - // Anonymous label references, i.e.: jmp @b - 'label-reference-anonymous': { - pattern: /(!\s*j[a-z]+\s+)@[fb]/i, - lookbehind: true, - alias: 'fasm-label', - }, - // Named label reference, i.e.: jne label1 - 'label-reference-addressed': { - pattern: /(!\s*j[a-z]+\s+)[A-Z._?$@][\w.?$@~#]*/i, - lookbehind: true, - alias: 'fasm-label', - }, - 'keyword': [/\b(?:extern|global)\b[^;\r\n]*/i, /\b(?:CPU|DEFAULT|FLOAT)\b.*/], - 'function': { - pattern: /^([\t ]*!\s*)[\da-z]+(?=\s|$)/im, - lookbehind: true, - }, - 'function-inline': { - pattern: /(:\s*)[\da-z]+(?=\s)/i, - lookbehind: true, - alias: 'function', - }, - 'label': { - pattern: /^([\t ]*!\s*)[A-Za-z._?$@][\w.?$@~#]*(?=:)/m, - lookbehind: true, - alias: 'fasm-label', - }, - 'register': - /\b(?:st\d|[xyz]mm\d\d?|[cdt]r\d|r\d\d?[bwd]?|[abcd][hl]|[er]?[abcd]x|[er]?(?:bp|di|si|sp)|[cdefgs]s|mm\d+)\b/i, - 'number': - /(?:\b|-|(?=\$))(?:0[hx](?:[\da-f]*\.)?[\da-f]+(?:p[+-]?\d+)?|\d[\da-f]+[hx]|\$\d[\da-f]*|0[oq][0-7]+|[0-7]+[oq]|0[by][01]+|[01]+[by]|0[dt]\d+|(?:\d+(?:\.\d+)?|\.\d+)(?:\.?e[+-]?\d+)?[dt]?)\b/i, - 'operator': /[\[\]*+\-/%<>=&|$!,.:]/, - }, - }, - }); - - delete base['class-name']; - delete base['boolean']; - return { 'comment': /;.*/, 'keyword': @@ -74,6 +20,63 @@ export default { 'function': /\b\w+(?:\.\w+)?\s*(?=\()/, 'number': /(?:\$[\da-f]+|\b-?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)\b/i, 'operator': /(?:@\*?|\?|\*)\w+\$?|-[>-]?|\+\+?|!=?|<>?=?|==?|&&?|\|?\||[~^%?*/@]/, + + // PureBasic support, steal stuff from ansi-c + $insert: { + 'tag': { + $before: 'keyword', + pattern: /#\w+\$?/, + }, + 'asm': { + $before: 'keyword', + pattern: /(^[\t ]*)!.*/m, + lookbehind: true, + alias: 'tag', + inside: { + 'comment': /;.*/, + 'string': { + pattern: /(["'`])(?:\\.|(?!\1)[^\\\r\n])*\1/, + greedy: true, + }, + // Anonymous label references, i.e.: jmp @b + 'label-reference-anonymous': { + pattern: /(!\s*j[a-z]+\s+)@[fb]/i, + lookbehind: true, + alias: 'fasm-label', + }, + // Named label reference, i.e.: jne label1 + 'label-reference-addressed': { + pattern: /(!\s*j[a-z]+\s+)[A-Z._?$@][\w.?$@~#]*/i, + lookbehind: true, + alias: 'fasm-label', + }, + 'keyword': [ + /\b(?:extern|global)\b[^;\r\n]*/i, + /\b(?:CPU|DEFAULT|FLOAT)\b.*/, + ], + 'function': { + pattern: /^([\t ]*!\s*)[\da-z]+(?=\s|$)/im, + lookbehind: true, + }, + 'function-inline': { + pattern: /(:\s*)[\da-z]+(?=\s)/i, + lookbehind: true, + alias: 'function', + }, + 'label': { + pattern: /^([\t ]*!\s*)[A-Za-z._?$@][\w.?$@~#]*(?=:)/m, + lookbehind: true, + alias: 'fasm-label', + }, + 'register': + /\b(?:st\d|[xyz]mm\d\d?|[cdt]r\d|r\d\d?[bwd]?|[abcd][hl]|[er]?[abcd]x|[er]?(?:bp|di|si|sp)|[cdefgs]s|mm\d+)\b/i, + 'number': + /(?:\b|-|(?=\$))(?:0[hx](?:[\da-f]*\.)?[\da-f]+(?:p[+-]?\d+)?|\d[\da-f]+[hx]|\$\d[\da-f]*|0[oq][0-7]+|[0-7]+[oq]|0[by][01]+|[01]+[by]|0[dt]\d+|(?:\d+(?:\.\d+)?|\.\d+)(?:\.?e[+-]?\d+)?[dt]?)\b/i, + 'operator': /[\[\]*+\-/%<>=&|$!,.:]/, + }, + }, + }, + $delete: ['class-name', 'boolean'], }; }, }; diff --git a/src/languages/python.js b/src/languages/python.js index caf6c49840..d1beb9605e 100644 --- a/src/languages/python.js +++ b/src/languages/python.js @@ -2,7 +2,7 @@ export default { id: 'python', alias: 'py', - grammar: /** @type {Grammar} */ ({ + grammar: { 'comment': { pattern: /(^|[^\\])#.*/, lookbehind: true, @@ -26,7 +26,7 @@ export default { pattern: /![sra](?=[:}]$)/, alias: 'punctuation', }, - $rest: /** @type {Grammar['$rest']} */ ('python'), + $rest: 'python', }, }, 'string': /[\s\S]+/, @@ -66,9 +66,5 @@ export default { /\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i, 'operator': /[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/, 'punctuation': /[{}[\];(),.:]/, - }), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/qsharp.js b/src/languages/qsharp.js index 33eb2aff21..c69377dc57 100644 --- a/src/languages/qsharp.js +++ b/src/languages/qsharp.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'qsharp'>} */ @@ -6,7 +5,7 @@ export default { id: 'qsharp', base: clike, alias: 'qs', - grammar ({ base }) { + grammar () { /** * Replaces all placeholders "<>" of given pattern with the n-th replacement (zero based). * @@ -78,41 +77,12 @@ export default { // strings const regularString = /"(?:\\.|[^\\"])*"/.source; - insertBefore(base, 'number', { - 'range': { - pattern: /\.\./, - alias: 'operator', - }, - }); - // single line const interpolationExpr = nested( replace(/\{(?:[^"{}]|<<0>>|<>)*\}/.source, [regularString]), 2 ); - insertBefore(base, 'string', { - 'interpolation-string': { - pattern: re(/\$"(?:\\.|<<0>>|[^\\"{])*"/.source, [interpolationExpr]), - greedy: true, - inside: { - 'interpolation': { - pattern: re(/((?:^|[^\\])(?:\\\\)*)<<0>>/.source, [interpolationExpr]), - lookbehind: true, - inside: { - 'punctuation': /^\{|\}$/, - 'expression': { - pattern: /[\s\S]+/, - alias: 'language-qsharp', - inside: 'qsharp', - }, - }, - }, - 'string': /[\s\S]+/, - }, - }, - }); - return { 'comment': /\/\/.*/, 'string': [ @@ -143,6 +113,33 @@ export default { 'operator': /\band=|\bor=|\band\b|\bnot\b|\bor\b|<[-=]|[-=]>|>>>=?|<<<=?|\^\^\^=?|\|\|\|=?|&&&=?|w\/=?|~~~|[*\/+\-^=!%]=?/, 'punctuation': /::|[{}[\];(),.:]/, + $insert: { + 'range': { + $before: 'number', + pattern: /\.\./, + alias: 'operator', + }, + 'interpolation-string': { + $before: 'string', + pattern: re(/\$"(?:\\.|<<0>>|[^\\"{])*"/.source, [interpolationExpr]), + greedy: true, + inside: { + 'interpolation': { + pattern: re(/((?:^|[^\\])(?:\\\\)*)<<0>>/.source, [interpolationExpr]), + lookbehind: true, + inside: { + 'punctuation': /^\{|\}$/, + 'expression': { + pattern: /[\s\S]+/, + alias: 'language-qsharp', + inside: 'qsharp', + }, + }, + }, + 'string': /[\s\S]+/, + }, + }, + }, }; }, }; diff --git a/src/languages/racket.js b/src/languages/racket.js index 48dbe2ac53..47bfae84d1 100644 --- a/src/languages/racket.js +++ b/src/languages/racket.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import scheme from './scheme.js'; /** @type {import('../types.d.ts').LanguageProto<'racket'>} */ @@ -6,15 +5,7 @@ export default { id: 'racket', base: scheme, alias: 'rkt', - grammar ({ base }) { - insertBefore(base, 'string', { - 'lang': { - pattern: /^#lang.+/m, - greedy: true, - alias: 'keyword', - }, - }); - + grammar () { return { 'lambda-parameter': { // the racket lambda syntax is a lot more complex, so we won't even attempt to capture it. @@ -22,6 +13,14 @@ export default { pattern: /([(\[]lambda\s+[(\[])[^()\[\]'\s]+/, lookbehind: true, }, + $insert: { + 'lang': { + $before: 'string', + pattern: /^#lang.+/m, + greedy: true, + alias: 'keyword', + }, + }, }; }, }; diff --git a/src/languages/reason.js b/src/languages/reason.js index 08084fa8ce..45e718387b 100644 --- a/src/languages/reason.js +++ b/src/languages/reason.js @@ -1,27 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'reason'>} */ export default { id: 'reason', base: clike, - grammar ({ base }) { - insertBefore(base, 'class-name', { - 'char': { - pattern: /'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/, - greedy: true, - }, - // Negative look-ahead prevents from matching things like String.capitalize - 'constructor': /\b[A-Z]\w*\b(?!\s*\.)/, - 'label': { - pattern: /\b[a-z]\w*(?=::)/, - alias: 'symbol', - }, - }); - - // We can't match functions property, so let's not even try. - delete base.function; - + grammar () { return { 'string': { pattern: /"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/, @@ -33,6 +16,24 @@ export default { /\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/, 'operator': /\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/, + + // We can't match functions property, so let's not even try. + $delete: ['function'], + + $insertBefore: { + 'class-name': { + 'char': { + pattern: /'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/, + greedy: true, + }, + // Negative look-ahead prevents from matching things like String.capitalize + 'constructor': /\b[A-Z]\w*\b(?!\s*\.)/, + 'label': { + pattern: /\b[a-z]\w*(?=::)/, + alias: 'symbol', + }, + }, + }, }; }, }; diff --git a/src/languages/rescript.js b/src/languages/rescript.js index 08bc3d1249..5981edcb4b 100644 --- a/src/languages/rescript.js +++ b/src/languages/rescript.js @@ -1,16 +1,36 @@ -import { insertBefore } from '../util/language-util.js'; - /** @type {import('../types.d.ts').LanguageProto<'rescript'>} */ export default { id: 'rescript', alias: 'res', grammar () { - const rescript = { + return { 'comment': { pattern: /\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/, greedy: true, }, 'char': { pattern: /'(?:[^\r\n\\]|\\(?:.|\w+))'/, greedy: true }, + 'template-string': { + pattern: /`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/, + greedy: true, + inside: { + 'template-punctuation': { + pattern: /^`|`$/, + alias: 'string', + }, + 'interpolation': { + pattern: /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/, + lookbehind: true, + inside: { + 'interpolation-punctuation': { + pattern: /^\$\{|\}$/, + alias: 'tag', + }, + $rest: 'rescript', + }, + }, + 'string': /[\s\S]+/, + }, + }, 'string': { pattern: /"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/, greedy: true, @@ -41,36 +61,5 @@ export default { /\.{3}|:[:=]?|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/, 'punctuation': /[(){}[\],;.]/, }; - - insertBefore(rescript, 'string', { - 'template-string': { - pattern: /`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/, - greedy: true, - inside: { - 'template-punctuation': { - pattern: /^`|`$/, - alias: 'string', - }, - 'interpolation': { - pattern: /((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/, - lookbehind: true, - inside: /** @type {Grammar} */ ({ - 'interpolation-punctuation': { - pattern: /^\$\{|\}$/, - alias: 'tag', - }, - $rest: /** @type {Grammar['$rest']} */ ('rescript'), - }), - }, - 'string': /[\s\S]+/, - }, - }, - }); - - return rescript; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/ruby.js b/src/languages/ruby.js index 1a4986ce89..0ca0266b41 100644 --- a/src/languages/ruby.js +++ b/src/languages/ruby.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'ruby'>} */ @@ -6,19 +5,13 @@ export default { id: 'ruby', base: clike, alias: 'rb', - grammar ({ base }) { + grammar () { /** * Original by Samuel Flores * * Adds the following new token classes: * constant, builtin, variable, symbol, regex */ - insertBefore(base, 'operator', { - 'double-colon': { - pattern: /::/, - alias: 'punctuation', - }, - }); const interpolation = { pattern: /((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/, @@ -36,8 +29,6 @@ export default { }, }; - delete base.function; - const percentExpression = '(?:' + [ @@ -52,153 +43,162 @@ export default { const symbolName = /(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/ .source; - insertBefore(base, 'keyword', { - 'regex-literal': [ - { - pattern: RegExp(/%r/.source + percentExpression + /[egimnosux]{0,6}/.source), - greedy: true, - inside: { - 'interpolation': interpolation, - 'regex': /[\s\S]+/, - }, - }, - { - pattern: - /(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/, - lookbehind: true, - greedy: true, - inside: { - 'interpolation': interpolation, - 'regex': /[\s\S]+/, - }, - }, - ], - 'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/, - 'symbol': [ - { - pattern: RegExp(/(^|[^:]):/.source + symbolName), - lookbehind: true, - greedy: true, - }, - { - pattern: RegExp(/([\r\n{(,][ \t]*)/.source + symbolName + /(?=:(?!:))/.source), - lookbehind: true, - greedy: true, - }, - ], - 'method-definition': { - pattern: /(\bdef\s+)\w+(?:\s*\.\s*\w+)?/, + return { + 'comment': { + pattern: /#.*|^=begin\s[\s\S]*?^=end/m, + greedy: true, + }, + 'class-name': { + pattern: + /(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/, lookbehind: true, inside: { - 'function': /\b\w+$/, - 'keyword': /^self\b/, - 'class-name': /^\w+/, - 'punctuation': /\./, + 'punctuation': /[.\\]/, }, }, - }); - - insertBefore(base, 'string', { - 'string-literal': [ - { - pattern: RegExp(/%[qQiIwWs]?/.source + percentExpression), - greedy: true, - inside: { - 'interpolation': interpolation, - 'string': /[\s\S]+/, - }, - }, - { - pattern: /("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/, - greedy: true, - inside: { - 'interpolation': interpolation, - 'string': /[\s\S]+/, + 'keyword': + /\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/, + 'operator': /\.{2,3}|&\.|===||[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/, + 'punctuation': /[(){}[\].,;]/, + $insertBefore: { + 'operator': { + 'double-colon': { + pattern: /::/, + alias: 'punctuation', }, }, - { - pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i, - alias: 'heredoc-string', - greedy: true, - inside: { - 'delimiter': { - pattern: /^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i, + 'keyword': { + 'regex-literal': [ + { + pattern: RegExp( + /%r/.source + percentExpression + /[egimnosux]{0,6}/.source + ), + greedy: true, inside: { - 'symbol': /\b\w+/, - 'punctuation': /^<<[-~]?/, + 'interpolation': interpolation, + 'regex': /[\s\S]+/, }, }, - 'interpolation': interpolation, - 'string': /[\s\S]+/, - }, - }, - { - pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i, - alias: 'heredoc-string', - greedy: true, - inside: { - 'delimiter': { - pattern: /^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i, + { + pattern: + /(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/, + lookbehind: true, + greedy: true, inside: { - 'symbol': /\b\w+/, - 'punctuation': /^<<[-~]?'|'$/, + 'interpolation': interpolation, + 'regex': /[\s\S]+/, }, }, - 'string': /[\s\S]+/, - }, - }, - ], - 'command-literal': [ - { - pattern: RegExp(/%x/.source + percentExpression), - greedy: true, - inside: { - 'interpolation': interpolation, - 'command': { - pattern: /[\s\S]+/, - alias: 'string', + ], + 'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/, + 'symbol': [ + { + pattern: RegExp(/(^|[^:]):/.source + symbolName), + lookbehind: true, + greedy: true, + }, + { + pattern: RegExp( + /([\r\n{(,][ \t]*)/.source + symbolName + /(?=:(?!:))/.source + ), + lookbehind: true, + greedy: true, + }, + ], + 'method-definition': { + pattern: /(\bdef\s+)\w+(?:\s*\.\s*\w+)?/, + lookbehind: true, + inside: { + 'function': /\b\w+$/, + 'keyword': /^self\b/, + 'class-name': /^\w+/, + 'punctuation': /\./, }, }, }, - { - pattern: /`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/, - greedy: true, - inside: { - 'interpolation': interpolation, - 'command': { - pattern: /[\s\S]+/, - alias: 'string', + 'string': { + 'string-literal': [ + { + pattern: RegExp(/%[qQiIwWs]?/.source + percentExpression), + greedy: true, + inside: { + 'interpolation': interpolation, + 'string': /[\s\S]+/, + }, }, - }, + { + pattern: + /("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/, + greedy: true, + inside: { + 'interpolation': interpolation, + 'string': /[\s\S]+/, + }, + }, + { + pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i, + alias: 'heredoc-string', + greedy: true, + inside: { + 'delimiter': { + pattern: /^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i, + inside: { + 'symbol': /\b\w+/, + 'punctuation': /^<<[-~]?/, + }, + }, + 'interpolation': interpolation, + 'string': /[\s\S]+/, + }, + }, + { + pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i, + alias: 'heredoc-string', + greedy: true, + inside: { + 'delimiter': { + pattern: /^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i, + inside: { + 'symbol': /\b\w+/, + 'punctuation': /^<<[-~]?'|'$/, + }, + }, + 'string': /[\s\S]+/, + }, + }, + ], + 'command-literal': [ + { + pattern: RegExp(/%x/.source + percentExpression), + greedy: true, + inside: { + 'interpolation': interpolation, + 'command': { + pattern: /[\s\S]+/, + alias: 'string', + }, + }, + }, + { + pattern: /`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/, + greedy: true, + inside: { + 'interpolation': interpolation, + 'command': { + pattern: /[\s\S]+/, + alias: 'string', + }, + }, + }, + ], }, - ], - }); - - delete base.string; - - insertBefore(base, 'number', { - 'builtin': - /\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/, - 'constant': /\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/, - }); - - return { - 'comment': { - pattern: /#.*|^=begin\s[\s\S]*?^=end/m, - greedy: true, - }, - 'class-name': { - pattern: - /(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/, - lookbehind: true, - inside: { - 'punctuation': /[.\\]/, + 'number': { + 'builtin': + /\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/, + 'constant': /\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/, }, }, - 'keyword': - /\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/, - 'operator': /\.{2,3}|&\.|===||[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/, - 'punctuation': /[(){}[\].,;]/, + $delete: ['function', 'string'], }; }, }; diff --git a/src/languages/rust.js b/src/languages/rust.js index 8529998a51..1e026d5e12 100644 --- a/src/languages/rust.js +++ b/src/languages/rust.js @@ -14,7 +14,7 @@ export default { greedy: true, }; - return /** @type {Grammar} */ ({ + return { 'comment': [ { pattern: RegExp(/(^|[^\\])/.source + multilineComment), @@ -51,7 +51,7 @@ export default { pattern: /^\||\|$/, alias: 'punctuation', }, - $rest: /** @type {Grammar['$rest']} */ ('rust'), + $rest: 'rust', }, }, @@ -125,10 +125,6 @@ export default { 'boolean': /\b(?:false|true)\b/, 'punctuation': /->|\.\.=|\.{1,3}|::|[{}[\];(),:]/, 'operator': /[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/, - }); + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/sas.js b/src/languages/sas.js index ece2f52a3a..8dc50d3d57 100644 --- a/src/languages/sas.js +++ b/src/languages/sas.js @@ -141,7 +141,7 @@ export default { lookbehind: true, }; - return /** @type {Grammar} */ ({ + return { 'datalines': { pattern: /^([ \t]*)(?:cards|(?:data)?lines);[\s\S]+?^[ \t]*;/im, lookbehind: true, @@ -256,7 +256,7 @@ export default { keyword: /^(?:saveresult)/i, }, }, - $rest: /** @type {Grammar['$rest']} */ (args), + $rest: args, }, }, 'cas-actions': casActions, @@ -365,10 +365,6 @@ export default { 'number': number, 'operator': /\*\*?|\|\|?|!!?|¦¦?|<[>=]?|>[<=]?|[-+\/=&]|[~¬^]=?/, 'punctuation': punctuation, - }); + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/sass.js b/src/languages/sass.js index 2aaae27341..05abf61b25 100644 --- a/src/languages/sass.js +++ b/src/languages/sass.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import css from './css.js'; /** @type {import('../types.d.ts').LanguageProto<'sass'>} */ @@ -6,19 +5,6 @@ export default { id: 'sass', base: css, grammar ({ base }) { - insertBefore(base, 'atrule', { - // We want to consume the whole line - 'atrule-line': { - // Includes support for = and + shortcuts - pattern: /^(?:[ \t]*)[@+=].+/m, - greedy: true, - inside: { - 'atrule': /(?:@[\w-]+|[+=])/, - }, - }, - }); - delete base.atrule; - const variable = /\$[-\w]+|#\{\$[-\w]+\}/; const operator = [ /[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/, @@ -28,50 +14,6 @@ export default { }, ]; - insertBefore(base, 'property', { - // We want to consume the whole line - 'variable-line': { - pattern: /^[ \t]*\$.+/m, - greedy: true, - inside: { - 'punctuation': /:/, - 'variable': variable, - 'operator': operator, - }, - }, - // We want to consume the whole line - 'property-line': { - pattern: /^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m, - greedy: true, - inside: { - 'property': [ - /[^:\s]+(?=\s*:)/, - { - pattern: /(:)[^:\s]+/, - lookbehind: true, - }, - ], - 'punctuation': /:/, - 'variable': variable, - 'operator': operator, - 'important': base.important, - }, - }, - }); - delete base.property; - delete base.important; - - // Now that whole lines for other patterns are consumed, - // what's left should be selectors - insertBefore(base, 'punctuation', { - 'selector': { - pattern: - /^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m, - lookbehind: true, - greedy: true, - }, - }); - return { // Sass comments don't need to be closed, only indented 'comment': { @@ -79,6 +21,58 @@ export default { lookbehind: true, greedy: true, }, + $insert: { + // We want to consume the whole line + 'atrule-line': { + $before: 'atrule', + // Includes support for = and + shortcuts + pattern: /^(?:[ \t]*)[@+=].+/m, + greedy: true, + inside: { + 'atrule': /(?:@[\w-]+|[+=])/, + }, + }, + // We want to consume the whole line + 'variable-line': { + $before: 'property', + pattern: /^[ \t]*\$.+/m, + greedy: true, + inside: { + 'punctuation': /:/, + 'variable': variable, + 'operator': operator, + }, + }, + // We want to consume the whole line + 'property-line': { + $before: 'property', + pattern: /^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m, + greedy: true, + inside: { + 'property': [ + /[^:\s]+(?=\s*:)/, + { + pattern: /(:)[^:\s]+/, + lookbehind: true, + }, + ], + 'punctuation': /:/, + 'variable': variable, + 'operator': operator, + 'important': base.important, + }, + }, + // Now that whole lines for other patterns are consumed, + // what's left should be selectors + 'selector': { + $before: 'punctuation', + pattern: + /^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m, + lookbehind: true, + greedy: true, + }, + }, + $delete: ['atrule', 'property', 'important'], }; }, }; diff --git a/src/languages/scala.js b/src/languages/scala.js index 3ffe8e8f61..aa4695141d 100644 --- a/src/languages/scala.js +++ b/src/languages/scala.js @@ -1,48 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import java from './java.js'; /** @type {import('../types.d.ts').LanguageProto<'scala'>} */ export default { id: 'scala', base: java, - grammar ({ base }) { - insertBefore(base, 'triple-quoted-string', { - 'string-interpolation': { - pattern: - /\b[a-z]\w*(?:"""(?:[^$]|\$(?:[^{]|\{(?:[^{}]|\{[^{}]*\})*\}))*?"""|"(?:[^$"\r\n]|\$(?:[^{]|\{(?:[^{}]|\{[^{}]*\})*\}))*")/i, - greedy: true, - inside: { - 'id': { - pattern: /^\w+/, - greedy: true, - alias: 'function', - }, - 'escape': { - pattern: /\\\$"|\$[$"]/, - greedy: true, - alias: 'symbol', - }, - 'interpolation': { - pattern: /\$(?:\w+|\{(?:[^{}]|\{[^{}]*\})*\})/, - greedy: true, - inside: { - 'punctuation': /^\$\{?|\}$/, - 'expression': { - pattern: /[\s\S]+/, - inside: 'scala', - }, - }, - }, - 'string': /[\s\S]+/, - }, - }, - }); - - delete base['doc-comment']; - delete base['class-name']; - delete base['function']; - delete base['constant']; - + grammar () { return { 'triple-quoted-string': { pattern: /"""[\s\S]*?"""/, @@ -59,6 +21,39 @@ export default { 'builtin': /\b(?:Any|AnyRef|AnyVal|Boolean|Byte|Char|Double|Float|Int|Long|Nothing|Short|String|Unit)\b/, 'symbol': /'[^\d\s\\]\w*/, + $insert: { + 'string-interpolation': { + $before: 'triple-quoted-string', + pattern: + /\b[a-z]\w*(?:"""(?:[^$]|\$(?:[^{]|\{(?:[^{}]|\{[^{}]*\})*\}))*?"""|"(?:[^$"\r\n]|\$(?:[^{]|\{(?:[^{}]|\{[^{}]*\})*\}))*")/i, + greedy: true, + inside: { + 'id': { + pattern: /^\w+/, + greedy: true, + alias: 'function', + }, + 'escape': { + pattern: /\\\$"|\$[$"]/, + greedy: true, + alias: 'symbol', + }, + 'interpolation': { + pattern: /\$(?:\w+|\{(?:[^{}]|\{[^{}]*\})*\})/, + greedy: true, + inside: { + 'punctuation': /^\$\{?|\}$/, + 'expression': { + pattern: /[\s\S]+/, + inside: 'scala', + }, + }, + }, + 'string': /[\s\S]+/, + }, + }, + }, + $delete: ['doc-comment', 'class-name', 'function', 'constant'], }; }, }; diff --git a/src/languages/scss.js b/src/languages/scss.js index a25455e920..e23fe29950 100644 --- a/src/languages/scss.js +++ b/src/languages/scss.js @@ -1,51 +1,11 @@ -import { insertBefore } from '../util/language-util.js'; import css from './css.js'; /** @type {import('../types.d.ts').LanguageProto<'scss'>} */ export default { id: 'scss', base: css, - grammar ({ base }) { - insertBefore(base, 'atrule', { - 'keyword': [ - /@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i, - { - pattern: /( )(?:from|through)(?= )/, - lookbehind: true, - }, - ], - }); - - insertBefore(base, 'important', { - // var and interpolated vars - 'variable': /\$[-\w]+|#\{\$[-\w]+\}/, - }); - - insertBefore(base, 'function', { - 'module-modifier': { - pattern: /\b(?:as|hide|show|with)\b/i, - alias: 'keyword', - }, - 'placeholder': { - pattern: /%[-\w]+/, - alias: 'selector', - }, - 'statement': { - pattern: /\B!(?:default|optional)\b/i, - alias: 'keyword', - }, - 'boolean': /\b(?:false|true)\b/, - 'null': { - pattern: /\bnull\b/, - alias: 'keyword', - }, - 'operator': { - pattern: /(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/, - lookbehind: true, - }, - }); - - return /** @type {Grammar} */ ({ + grammar () { + return { 'comment': { pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/, lookbehind: true, @@ -54,7 +14,7 @@ export default { pattern: /@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/, inside: { 'rule': /@[\w-]+/, - $rest: /** @type {Grammar['$rest']} */ ('scss'), + $rest: 'scss', }, }, // url, compassified @@ -85,10 +45,44 @@ export default { 'variable': /\$[-\w]+|#\{\$[-\w]+\}/, }, }, - }); + $insertBefore: { + 'atrule': { + 'keyword': [ + /@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i, + { + pattern: /( )(?:from|through)(?= )/, + lookbehind: true, + }, + ], + }, + 'important': { + // var and interpolated vars + 'variable': /\$[-\w]+|#\{\$[-\w]+\}/, + }, + 'function': { + 'module-modifier': { + pattern: /\b(?:as|hide|show|with)\b/i, + alias: 'keyword', + }, + 'placeholder': { + pattern: /%[-\w]+/, + alias: 'selector', + }, + 'statement': { + pattern: /\B!(?:default|optional)\b/i, + alias: 'keyword', + }, + 'boolean': /\b(?:false|true)\b/, + 'null': { + pattern: /\bnull\b/, + alias: 'keyword', + }, + 'operator': { + pattern: /(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/, + lookbehind: true, + }, + }, + }, + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/smarty.js b/src/languages/smarty.js index ed69757f7a..54fd0be8b1 100644 --- a/src/languages/smarty.js +++ b/src/languages/smarty.js @@ -82,7 +82,7 @@ export default { ) ); - return /** @type {Grammar} */ ({ + return { 'ignore-literal': { pattern: /(\{literal\})[\s\S]*?(?=\{\/literal\})/, lookbehind: true, @@ -105,12 +105,11 @@ export default { greedy: true, inside: smarty, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }); + $tokenize: embeddedIn('markup'), + }; }, }; /** - * @typedef {import('../types.d.ts').Grammar} Grammar * @typedef {import('../types.d.ts').GrammarToken} GrammarToken */ diff --git a/src/languages/solidity.js b/src/languages/solidity.js index 9a75bb086c..2d7057b852 100644 --- a/src/languages/solidity.js +++ b/src/languages/solidity.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'solidity'>} */ @@ -6,20 +5,7 @@ export default { id: 'solidity', base: clike, alias: 'sol', - grammar ({ base }) { - insertBefore(base, 'keyword', { - 'builtin': - /\b(?:address|bool|byte|u?int(?:8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?|string|bytes(?:[1-9]|[12]\d|3[0-2])?)\b/, - }); - - insertBefore(base, 'number', { - 'version': { - pattern: /([<>]=?|\^)\d+\.\d+\.\d+\b/, - lookbehind: true, - alias: 'number', - }, - }); - + grammar () { return { 'class-name': { pattern: /(\b(?:contract|enum|interface|library|new|struct|using)\s+)(?!\d)[\w$]+/, @@ -28,6 +14,19 @@ export default { 'keyword': /\b(?:_|anonymous|as|assembly|assert|break|calldata|case|constant|constructor|continue|contract|default|delete|do|else|emit|enum|event|external|for|from|function|if|import|indexed|inherited|interface|internal|is|let|library|mapping|memory|modifier|new|payable|pragma|private|public|pure|require|returns?|revert|selfdestruct|solidity|storage|struct|suicide|switch|this|throw|using|var|view|while)\b/, 'operator': /=>|->|:=|=:|\*\*|\+\+|--|\|\||&&|<<=?|>>=?|[-+*/%^&|<>!=]=?|[~?]/, + $insertBefore: { + 'keyword': { + 'builtin': + /\b(?:address|bool|byte|u?int(?:8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?|string|bytes(?:[1-9]|[12]\d|3[0-2])?)\b/, + }, + 'number': { + 'version': { + pattern: /([<>]=?|\^)\d+\.\d+\.\d+\b/, + lookbehind: true, + alias: 'number', + }, + }, + }, }; }, }; diff --git a/src/languages/soy.js b/src/languages/soy.js index 12a00011cf..1e4a970563 100644 --- a/src/languages/soy.js +++ b/src/languages/soy.js @@ -9,7 +9,7 @@ export default { const stringPattern = /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/; const numberPattern = /\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b|\b0x[\dA-F]+\b/; - return /** @type {Grammar} */ ({ + return { 'ignore-literal': { pattern: /(\{literal\})[\s\S]*?(?=\{\/literal\})/, lookbehind: true, @@ -82,11 +82,7 @@ export default { 'punctuation': /[{}()\[\]|.,:]/, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }); + $tokenize: embeddedIn('markup'), + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/sparql.js b/src/languages/sparql.js index ef4444052f..770ee68da1 100644 --- a/src/languages/sparql.js +++ b/src/languages/sparql.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import turtle from './turtle.js'; /** @type {import('../types.d.ts').LanguageProto<'sparql'>} */ @@ -6,21 +5,22 @@ export default { id: 'sparql', base: turtle, alias: 'rq', - grammar ({ base }) { - insertBefore(base, 'punctuation', { - 'keyword': [ - /\b(?:A|ADD|ALL|AS|ASC|ASK|BNODE|BY|CLEAR|CONSTRUCT|COPY|CREATE|DATA|DEFAULT|DELETE|DESC|DESCRIBE|DISTINCT|DROP|EXISTS|FILTER|FROM|GROUP|HAVING|INSERT|INTO|LIMIT|LOAD|MINUS|MOVE|NAMED|NOT|NOW|OFFSET|OPTIONAL|ORDER|RAND|REDUCED|SELECT|SEPARATOR|SERVICE|SILENT|STRUUID|UNION|USING|UUID|VALUES|WHERE)\b/i, - /\b(?:ABS|AVG|BIND|BOUND|CEIL|COALESCE|CONCAT|CONTAINS|COUNT|DATATYPE|DAY|ENCODE_FOR_URI|FLOOR|GROUP_CONCAT|HOURS|IF|IRI|isBLANK|isIRI|isLITERAL|isNUMERIC|isURI|LANG|LANGMATCHES|LCASE|MAX|MD5|MIN|MINUTES|MONTH|REGEX|REPLACE|ROUND|sameTerm|SAMPLE|SECONDS|SHA1|SHA256|SHA384|SHA512|STR|STRAFTER|STRBEFORE|STRDT|STRENDS|STRLANG|STRLEN|STRSTARTS|SUBSTR|SUM|TIMEZONE|TZ|UCASE|URI|YEAR)\b(?=\s*\()/i, - /\b(?:BASE|GRAPH|PREFIX)\b/i, - ], - }); - + grammar () { return { 'boolean': /\b(?:false|true)\b/i, 'variable': { pattern: /[?$]\w+/, greedy: true, }, + $insertBefore: { + 'punctuation': { + 'keyword': [ + /\b(?:A|ADD|ALL|AS|ASC|ASK|BNODE|BY|CLEAR|CONSTRUCT|COPY|CREATE|DATA|DEFAULT|DELETE|DESC|DESCRIBE|DISTINCT|DROP|EXISTS|FILTER|FROM|GROUP|HAVING|INSERT|INTO|LIMIT|LOAD|MINUS|MOVE|NAMED|NOT|NOW|OFFSET|OPTIONAL|ORDER|RAND|REDUCED|SELECT|SEPARATOR|SERVICE|SILENT|STRUUID|UNION|USING|UUID|VALUES|WHERE)\b/i, + /\b(?:ABS|AVG|BIND|BOUND|CEIL|COALESCE|CONCAT|CONTAINS|COUNT|DATATYPE|DAY|ENCODE_FOR_URI|FLOOR|GROUP_CONCAT|HOURS|IF|IRI|isBLANK|isIRI|isLITERAL|isNUMERIC|isURI|LANG|LANGMATCHES|LCASE|MAX|MD5|MIN|MINUTES|MONTH|REGEX|REPLACE|ROUND|sameTerm|SAMPLE|SECONDS|SHA1|SHA256|SHA384|SHA512|STR|STRAFTER|STRBEFORE|STRDT|STRENDS|STRLANG|STRLEN|STRSTARTS|SUBSTR|SUM|TIMEZONE|TZ|UCASE|URI|YEAR)\b(?=\s*\()/i, + /\b(?:BASE|GRAPH|PREFIX)\b/i, + ], + }, + }, }; }, }; diff --git a/src/languages/sqf.js b/src/languages/sqf.js index ac5a64dba4..e4ca2173e4 100644 --- a/src/languages/sqf.js +++ b/src/languages/sqf.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'sqf'>} */ @@ -6,24 +5,6 @@ export default { id: 'sqf', base: clike, grammar ({ base }) { - insertBefore(base, 'string', { - 'macro': { - pattern: /(^[ \t]*)#[a-z](?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im, - lookbehind: true, - greedy: true, - alias: 'property', - inside: { - 'directive': { - pattern: /#[a-z]+\b/i, - alias: 'keyword', - }, - 'comment': base.comment, - }, - }, - }); - - delete base['class-name']; - return { 'string': { pattern: /"(?:(?:"")?[^"])*"(?!")|'(?:[^'])*'/, @@ -42,6 +23,23 @@ export default { alias: 'keyword', }, 'constant': /\bDIK(?:_[a-z\d]+)+\b/i, + $insert: { + 'macro': { + $before: 'string', + pattern: /(^[ \t]*)#[a-z](?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im, + lookbehind: true, + greedy: true, + alias: 'property', + inside: { + 'directive': { + pattern: /#[a-z]+\b/i, + alias: 'keyword', + }, + 'comment': base.comment, + }, + }, + }, + $delete: ['class-name'], }; }, }; diff --git a/src/languages/squirrel.js b/src/languages/squirrel.js index 4c2cb3ab62..3353ab64a7 100644 --- a/src/languages/squirrel.js +++ b/src/languages/squirrel.js @@ -1,5 +1,4 @@ import { toArray } from '../util/iterables.js'; -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'squirrel'>} */ @@ -7,28 +6,9 @@ export default { id: 'squirrel', base: clike, grammar ({ base }) { - insertBefore(base, 'string', { - 'char': { - pattern: /(^|[^\\"'])'(?:[^\\']|\\(?:[xuU][0-9a-fA-F]{0,8}|[\s\S]))'/, - lookbehind: true, - greedy: true, - }, - }); - - insertBefore(base, 'operator', { - 'attribute-punctuation': { - pattern: /<\/|\/>/, - alias: 'important', - }, - 'lambda': { - pattern: /@(?=\()/, - alias: 'operator', - }, - }); - return { 'comment': [ - ...toArray(base.comment), + ...toArray(/** @type {import('../types.d.ts').GrammarTokens} */ (base).comment), { pattern: /#.*/, greedy: true, @@ -53,6 +33,24 @@ export default { 'number': /\b(?:0x[0-9a-fA-F]+|\d+(?:\.(?:\d+|[eE][+-]?\d+))?)\b/, 'operator': /\+\+|--|<=>|<[-<]|>>>?|&&?|\|\|?|[-+*/%!=<>]=?|[~^]|::?/, 'punctuation': /[(){}\[\],;.]/, + $insert: { + 'char': { + $before: 'string', + pattern: /(^|[^\\"'])'(?:[^\\']|\\(?:[xuU][0-9a-fA-F]{0,8}|[\s\S]))'/, + lookbehind: true, + greedy: true, + }, + 'attribute-punctuation': { + $before: 'operator', + pattern: /<\/|\/>/, + alias: 'important', + }, + 'lambda': { + $before: 'operator', + pattern: /@(?=\()/, + alias: 'operator', + }, + }, }; }, }; diff --git a/src/languages/stylus.js b/src/languages/stylus.js index 636c0441eb..be0a60ea4a 100644 --- a/src/languages/stylus.js +++ b/src/languages/stylus.js @@ -33,14 +33,14 @@ export default { pattern: /^\{|\}$/, alias: 'punctuation', }, - $rest: /** @type {Grammar['$rest']} */ (null), + $rest: null, }, }, 'func': { pattern: /[\w-]+\([^)]*\).*/, inside: { 'function': /^[^(]+/, - $rest: /** @type {Grammar['$rest']} */ (null), + $rest: null, }, }, 'important': /\B!(?:important|optional)\b/i, @@ -85,7 +85,7 @@ export default { lookbehind: true, inside: { 'atrule': /^@[\w-]+/, - $rest: /** @type {Grammar['$rest']} */ (inside), + $rest: inside, }, }, 'variable-declaration': { @@ -93,7 +93,7 @@ export default { lookbehind: true, inside: { 'variable': /^\S+/, - $rest: /** @type {Grammar['$rest']} */ (inside), + $rest: inside, }, }, @@ -102,7 +102,7 @@ export default { lookbehind: true, inside: { 'keyword': /^\S+/, - $rest: /** @type {Grammar['$rest']} */ (inside), + $rest: inside, }, }, @@ -119,7 +119,7 @@ export default { 'interpolation': inside.interpolation, }, }, - $rest: /** @type {Grammar['$rest']} */ (inside), + $rest: inside, }, }, diff --git a/src/languages/textile.js b/src/languages/textile.js index 1c02177ce8..070410b3a5 100644 --- a/src/languages/textile.js +++ b/src/languages/textile.js @@ -298,7 +298,7 @@ export default { Object.assign(phraseInside['table'].inside, nestedPatterns); // Only allow alpha-numeric HTML tags, not XML tags - const tag = /** @type {import('../types.d.ts').GrammarToken} */ (base.tag); + const tag = /** @type {GrammarToken} */ (base.tag); tag.pattern = /<\/?(?!\d)[a-z0-9]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i; diff --git a/src/languages/treeview.js b/src/languages/treeview.js index 98c87c3cc9..13da0d4bd6 100644 --- a/src/languages/treeview.js +++ b/src/languages/treeview.js @@ -5,7 +5,7 @@ import { withoutTokenize } from '../util/language-util.js'; export default { id: 'treeview', alias: 'tree-view', - grammar: /** @type {Grammar} */ ({ + grammar: { 'treeview-part': { pattern: /^.+/m, inside: { @@ -95,7 +95,7 @@ export default { }, }, }, - }), + }, }; /** diff --git a/src/languages/tt2.js b/src/languages/tt2.js index d5cee108ac..207c62b660 100644 --- a/src/languages/tt2.js +++ b/src/languages/tt2.js @@ -50,16 +50,12 @@ export default { // The different types of TT2 strings "replace" the C-like standard string delete tt2.string; - return /** @type {Grammar} */ ({ + return { 'tt2': { pattern: /\[%[\s\S]+?%\]/, inside: tt2, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }); + $tokenize: embeddedIn('markup'), + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/twig.js b/src/languages/twig.js index ba878e233f..11afabc76d 100644 --- a/src/languages/twig.js +++ b/src/languages/twig.js @@ -5,7 +5,7 @@ import markup from './markup.js'; export default { id: 'twig', require: markup, - grammar: /** @type {Grammar} */ ({ + grammar: { 'twig-comment': { pattern: /\{#[\s\S]*?#\}/, greedy: true, @@ -45,10 +45,6 @@ export default { 'punctuation': /[()\[\]{}:.,]/, }, }, - $tokenize: /** @type {Grammar['$tokenize']} */ (embeddedIn('markup')), - }), + $tokenize: embeddedIn('markup'), + }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/v.js b/src/languages/v.js index 2fdc9132a0..4c4bed29b1 100644 --- a/src/languages/v.js +++ b/src/languages/v.js @@ -1,54 +1,15 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'v'>} */ export default { id: 'v', base: clike, - grammar ({ base }) { - insertBefore(base, 'string', { - 'char': { - pattern: /`(?:\\`|\\?[^`]{1,2})`/, // using {1,2} instead of `u` flag for compatibility - alias: 'rune', - }, - }); - + grammar () { const genericInside = { 'punctuation': /[<>]/, 'class-name': /\w+/, }; - insertBefore(base, 'operator', { - 'attribute': { - pattern: - /(^[\t ]*)\[(?:deprecated|direct_array_access|flag|inline|live|ref_only|typedef|unsafe_fn|windows_stdcall)\]/m, - lookbehind: true, - alias: 'annotation', - inside: { - 'punctuation': /[\[\]]/, - 'keyword': /\w+/, - }, - }, - 'generic': { - pattern: /<\w+>(?=\s*[\)\{])/, - inside: genericInside, - }, - }); - - insertBefore(base, 'function', { - 'generic-function': { - // e.g. foo( ... - pattern: /\b\w+\s*<\w+>(?=\()/, - inside: { - 'function': /^\w+/, - 'generic': { - pattern: /<\w+>/, - inside: genericInside, - }, - }, - }, - }); - return { 'string': { pattern: /r?(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, @@ -88,6 +49,41 @@ export default { /~|\?|[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\.?/, 'builtin': /\b(?:any(?:_float|_int)?|bool|byte(?:ptr)?|charptr|f(?:32|64)|i(?:8|16|64|128|nt)|rune|size_t|string|u(?:16|32|64|128)|voidptr)\b/, + $insert: { + 'char': { + $before: 'string', + pattern: /`(?:\\`|\\?[^`]{1,2})`/, // using {1,2} instead of `u` flag for compatibility + alias: 'rune', + }, + 'attribute': { + $before: 'operator', + pattern: + /(^[\t ]*)\[(?:deprecated|direct_array_access|flag|inline|live|ref_only|typedef|unsafe_fn|windows_stdcall)\]/m, + lookbehind: true, + alias: 'annotation', + inside: { + 'punctuation': /[\[\]]/, + 'keyword': /\w+/, + }, + }, + 'generic': { + $before: 'operator', + pattern: /<\w+>(?=\s*[\)\{])/, + inside: genericInside, + }, + 'generic-function': { + $before: 'function', + // e.g. foo( ... + pattern: /\b\w+\s*<\w+>(?=\()/, + inside: { + 'function': /^\w+/, + 'generic': { + pattern: /<\w+>/, + inside: genericInside, + }, + }, + }, + }, }; }, }; diff --git a/src/languages/vala.js b/src/languages/vala.js index 7adb8d9b11..169a4260b8 100644 --- a/src/languages/vala.js +++ b/src/languages/vala.js @@ -1,54 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import clike from './clike.js'; /** @type {import('../types.d.ts').LanguageProto<'vala'>} */ export default { id: 'vala', base: clike, - grammar ({ base }) { - insertBefore(base, 'string', { - 'raw-string': { - pattern: /"""[\s\S]*?"""/, - greedy: true, - alias: 'string', - }, - 'template-string': { - pattern: /@"[\s\S]*?"/, - greedy: true, - inside: { - 'interpolation': { - pattern: /\$(?:\([^)]*\)|[a-zA-Z]\w*)/, - inside: /** @type {Grammar} */ ({ - 'delimiter': { - pattern: /^\$\(?|\)$/, - alias: 'punctuation', - }, - $rest: /** @type {Grammar['$rest']} */ ('vala'), - }), - }, - 'string': /[\s\S]+/, - }, - }, - }); - - insertBefore(base, 'keyword', { - 'regex': { - pattern: - /\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[imsx]{0,4}(?=\s*(?:$|[\r\n,.;})\]]))/, - greedy: true, - inside: { - 'regex-source': { - pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/, - lookbehind: true, - alias: 'language-regex', - inside: 'regex', - }, - 'regex-delimiter': /^\//, - 'regex-flags': /^[a-z]+$/, - }, - }, - }); - + grammar () { return { // Classes copied from csharp 'class-name': [ @@ -93,10 +49,48 @@ export default { 'operator': /\+\+|--|&&|\|\||<<=?|>>=?|=>|->|~|[+\-*\/%&^|=!<>]=?|\?\??|\.\.\./, 'punctuation': /[{}[\];(),.:]/, 'constant': /\b[A-Z0-9_]+\b/, + $insert: { + 'raw-string': { + $before: 'string', + pattern: /"""[\s\S]*?"""/, + greedy: true, + alias: 'string', + }, + 'template-string': { + $before: 'string', + pattern: /@"[\s\S]*?"/, + greedy: true, + inside: { + 'interpolation': { + pattern: /\$(?:\([^)]*\)|[a-zA-Z]\w*)/, + inside: { + 'delimiter': { + pattern: /^\$\(?|\)$/, + alias: 'punctuation', + }, + $rest: 'vala', + }, + }, + 'string': /[\s\S]+/, + }, + }, + 'regex': { + $before: 'keyword', + pattern: + /\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[imsx]{0,4}(?=\s*(?:$|[\r\n,.;})\]]))/, + greedy: true, + inside: { + 'regex-source': { + pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/, + lookbehind: true, + alias: 'language-regex', + inside: 'regex', + }, + 'regex-delimiter': /^\//, + 'regex-flags': /^[a-z]+$/, + }, + }, + }, }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - */ diff --git a/src/languages/vbnet.js b/src/languages/vbnet.js index e617b6fdbf..d554d3a4c0 100644 --- a/src/languages/vbnet.js +++ b/src/languages/vbnet.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import basic from './basic.js'; /** @type {import('../types.d.ts').LanguageProto<'vbnet'>} */ @@ -6,11 +5,7 @@ export default { id: 'vbnet', base: basic, optional: 'xml-doc', - grammar ({ base, getOptionalLanguage }) { - insertBefore(base, 'comment', { - 'doc-comment': getOptionalLanguage('xml-doc')?.tick, - }); - + grammar ({ getOptionalLanguage }) { return { 'comment': [ { @@ -33,6 +28,13 @@ export default { 'keyword': /(?:\b(?:ADDHANDLER|ADDRESSOF|ALIAS|AND|ANDALSO|AS|BEEP|BLOAD|BOOLEAN|BSAVE|BYREF|BYTE|BYVAL|CALL(?: ABSOLUTE)?|CASE|CATCH|CBOOL|CBYTE|CCHAR|CDATE|CDBL|CDEC|CHAIN|CHAR|CHDIR|CINT|CLASS|CLEAR|CLNG|CLOSE|CLS|COBJ|COM|COMMON|CONST|CONTINUE|CSBYTE|CSHORT|CSNG|CSTR|CTYPE|CUINT|CULNG|CUSHORT|DATA|DATE|DECIMAL|DECLARE|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DEFAULT|DELEGATE|DIM|DIRECTCAST|DO|DOUBLE|ELSE|ELSEIF|END|ENUM|ENVIRON|ERASE|ERROR|EVENT|EXIT|FALSE|FIELD|FILES|FINALLY|FOR(?: EACH)?|FRIEND|FUNCTION|GET|GETTYPE|GETXMLNAMESPACE|GLOBAL|GOSUB|GOTO|HANDLES|IF|IMPLEMENTS|IMPORTS|IN|INHERITS|INPUT|INTEGER|INTERFACE|IOCTL|IS|ISNOT|KEY|KILL|LET|LIB|LIKE|LINE INPUT|LOCATE|LOCK|LONG|LOOP|LSET|ME|MKDIR|MOD|MODULE|MUSTINHERIT|MUSTOVERRIDE|MYBASE|MYCLASS|NAME|NAMESPACE|NARROWING|NEW|NEXT|NOT|NOTHING|NOTINHERITABLE|NOTOVERRIDABLE|OBJECT|OF|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPEN|OPERATOR|OPTION(?: BASE)?|OPTIONAL|OR|ORELSE|OUT|OVERLOADS|OVERRIDABLE|OVERRIDES|PARAMARRAY|PARTIAL|POKE|PRIVATE|PROPERTY|PROTECTED|PUBLIC|PUT|RAISEEVENT|READ|READONLY|REDIM|REM|REMOVEHANDLER|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SBYTE|SELECT(?: CASE)?|SET|SHADOWS|SHARED|SHELL|SHORT|SINGLE|SLEEP|STATIC|STEP|STOP|STRING|STRUCTURE|SUB|SWAP|SYNCLOCK|SYSTEM|THEN|THROW|TIMER|TO|TROFF|TRON|TRUE|TRY|TRYCAST|TYPE|TYPEOF|UINTEGER|ULONG|UNLOCK|UNTIL|USHORT|USING|VIEW PRINT|WAIT|WEND|WHEN|WHILE|WIDENING|WITH|WITHEVENTS|WRITE|WRITEONLY|XOR)|\B(?:#CONST|#ELSE|#ELSEIF|#END|#IF))(?:\$|\b)/i, 'punctuation': /[,;:(){}]/, + $insertBefore: { + 'comment': { + 'doc-comment': /** @type {import('../types.d.ts').GrammarTokens} */ ( + getOptionalLanguage('xml-doc') + )?.tick, + }, + }, }; }, }; diff --git a/src/languages/velocity.js b/src/languages/velocity.js index 2c100c3ea3..70f56a2009 100644 --- a/src/languages/velocity.js +++ b/src/languages/velocity.js @@ -1,11 +1,10 @@ -import { insertBefore } from '../util/language-util.js'; import markup from './markup.js'; /** @type {import('../types.d.ts').LanguageProto<'velocity'>} */ export default { id: 'velocity', base: markup, - grammar ({ base }) { + grammar () { const vel = { 'variable': { pattern: @@ -34,59 +33,59 @@ export default { 'punctuation': vel['punctuation'], }; - insertBefore(base, 'comment', { - 'unparsed': { - pattern: /(^|[^\\])#\[\[[\s\S]*?\]\]#/, - lookbehind: true, - greedy: true, - inside: { - 'punctuation': /^#\[\[|\]\]#$/, + return { + $merge: { + 'tag': { + inside: { + 'attr-value': { + inside: { + $rest: 'velocity', + }, + }, + }, }, }, - 'velocity-comment': [ - { - pattern: /(^|[^\\])#\*[\s\S]*?\*#/, - lookbehind: true, - greedy: true, - alias: 'comment', - }, - { - pattern: /(^|[^\\])##.*/, - lookbehind: true, - greedy: true, - alias: 'comment', - }, - ], - 'directive': { - pattern: - /(^|[^\\](?:\\\\)*)#@?(?:[a-z][\w-]*|\{[a-z][\w-]*\})(?:\s*\((?:[^()]|\([^()]*\))*\))?/i, - lookbehind: true, - inside: /** @type {Grammar} */ ({ - 'keyword': { - pattern: /^#@?(?:[a-z][\w-]*|\{[a-z][\w-]*\})|\bin\b/, + $insertBefore: { + 'comment': { + 'unparsed': { + pattern: /(^|[^\\])#\[\[[\s\S]*?\]\]#/, + lookbehind: true, + greedy: true, + inside: { + 'punctuation': /^#\[\[|\]\]#$/, + }, + }, + 'velocity-comment': [ + { + pattern: /(^|[^\\])#\*[\s\S]*?\*#/, + lookbehind: true, + greedy: true, + alias: 'comment', + }, + { + pattern: /(^|[^\\])##.*/, + lookbehind: true, + greedy: true, + alias: 'comment', + }, + ], + 'directive': { + pattern: + /(^|[^\\](?:\\\\)*)#@?(?:[a-z][\w-]*|\{[a-z][\w-]*\})(?:\s*\((?:[^()]|\([^()]*\))*\))?/i, + lookbehind: true, inside: { - 'punctuation': /[{}]/, + 'keyword': { + pattern: /^#@?(?:[a-z][\w-]*|\{[a-z][\w-]*\})|\bin\b/, + inside: { + 'punctuation': /[{}]/, + }, + }, + $rest: vel, }, }, - $rest: /** @type {Grammar['$rest']} */ (vel), - }), + 'variable': vel['variable'], + }, }, - 'variable': vel['variable'], - }); - - /** @type {Grammar} */ ( - /** @type {GrammarToken} */ ( - /** @type {Grammar} */ (/** @type {GrammarToken} */ (base['tag']).inside)[ - 'attr-value' - ] - ).inside - ).$rest = 'velocity'; - - return {}; + }; }, }; - -/** - * @typedef {import('../types.d.ts').Grammar} Grammar - * @typedef {import('../types.d.ts').GrammarToken} GrammarToken - */ diff --git a/src/languages/wiki.js b/src/languages/wiki.js index a7960436aa..3edde2eab8 100644 --- a/src/languages/wiki.js +++ b/src/languages/wiki.js @@ -1,4 +1,3 @@ -import { insertBefore } from '../util/language-util.js'; import markup from './markup.js'; /** @type {import('../types.d.ts').LanguageProto<'wiki'>} */ @@ -6,22 +5,9 @@ export default { id: 'wiki', base: markup, grammar ({ base }) { - const tag = /** @type {GrammarToken} */ (base['tag']); + const tag = /** @type {import('../types.d.ts').GrammarToken} */ (base['tag']); - insertBefore(base, 'tag', { - // Prevent highlighting inside , and
 tags
-			'nowiki': {
-				pattern: /<(nowiki|pre|source)\b[^>]*>[\s\S]*?<\/\1>/i,
-				inside: {
-					'tag': {
-						pattern: /<(?:nowiki|pre|source)\b[^>]*>|<\/(?:nowiki|pre|source)>/i,
-						inside: tag.inside,
-					},
-				},
-			},
-		});
-
-		return /** @type {Grammar} */ ({
+		return {
 			'block-comment': {
 				pattern: /(^|[^\\])\/\*[\s\S]*?\*\//,
 				lookbehind: true,
@@ -86,11 +72,19 @@ export default {
 				},
 			},
 			'punctuation': /^(?:\{\||\|\}|\|-|[*#:;!|])|\|\||!!/m,
-		});
+			$insert: {
+				// Prevent highlighting inside ,  and 
 tags
+				'nowiki': {
+					$before: 'tag',
+					pattern: /<(nowiki|pre|source)\b[^>]*>[\s\S]*?<\/\1>/i,
+					inside: {
+						'tag': {
+							pattern: /<(?:nowiki|pre|source)\b[^>]*>|<\/(?:nowiki|pre|source)>/i,
+							inside: tag.inside,
+						},
+					},
+				},
+			},
+		};
 	},
 };
-
-/**
- * @typedef {import('../types.d.ts').Grammar} Grammar
- * @typedef {import('../types.d.ts').GrammarToken} GrammarToken
- */