// Docs: // https://docs.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-5.0&tabs=visual-studio // https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0 (function (Prism) { var commentLike = /\/(?![/*])|\/\/.*[\r\n]|\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\//.source; var stringLike = /@(?!")|"(?:[^\r\n\\"]|\\.)*"|@"(?:[^\\"]|""|\\[\s\S])*"(?!")/.source + '|' + /'(?:(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'|(?=[^\\](?!')))/.source; /** * Creates a nested pattern where all occurrences of the string `<<self>>` are replaced with the pattern itself. * * @param {string} pattern * @param {number} depthLog2 * @returns {string} */ function nested(pattern, depthLog2) { for (var i = 0; i < depthLog2; i++) { pattern = pattern.replace(/<self>/g, function () { return '(?:' + pattern + ')'; }); } return pattern .replace(/<self>/g, '[^\\s\\S]') .replace(/<str>/g, '(?:' + stringLike + ')') .replace(/<comment>/g, '(?:' + commentLike + ')'); } var round = nested(/\((?:[^()'"@/]|<str>|<comment>|<self>)*\)/.source, 2); var square = nested(/\[(?:[^\[\]'"@/]|<str>|<comment>|<self>)*\]/.source, 1); var curly = nested(/\{(?:[^{}'"@/]|<str>|<comment>|<self>)*\}/.source, 2); var angle = nested(/<(?:[^<>'"@/]|<comment>|<self>)*>/.source, 1); var inlineCs = /@/.source + /(?:await\b\s*)?/.source + '(?:' + /(?!await\b)\w+\b/.source + '|' + round + ')' + '(?:' + /[?!]?\.\w+\b/.source + '|' + '(?:' + angle + ')?' + round + '|' + square + ')*' + /(?![?!\.(\[]|<(?!\/))/.source; // Note about the above bracket patterns: // They all ignore HTML expressions that might be in the C# code. This is a problem because HTML (like strings and // comments) is parsed differently. This is a huge problem because HTML might contain brackets and quotes which // messes up the bracket and string counting implemented by the above patterns. // // This problem is not fixable because 1) HTML expression are highly context sensitive and very difficult to detect // and 2) they require one capturing group at every nested level. See the `tagRegion` pattern to admire the // complexity of an HTML expression. // // To somewhat alleviate the problem a bit, the patterns for characters (e.g. 'a') is very permissive, it also // allows invalid characters to support HTML expressions like this: <p>That's it!</p>. var tagAttrInlineCs = /@(?![\w()])/.source + '|' + inlineCs; var tagAttrValue = '(?:' + /"[^"@]*"|'[^'@]*'|[^\s'"@>=]+(?=[\s>])/.source + '|' + '["\'][^"\'@]*(?:(?:' + tagAttrInlineCs + ')[^"\'@]*)+["\']' + ')'; var tagAttrs = /(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*<tagAttrValue>|(?=[\s/>])))+)?/.source.replace(/<tagAttrValue>/, tagAttrValue); var tagContent = /(?!\d)[^\s>\/=$<%]+/.source + tagAttrs + /\s*\/?>/.source; var tagRegion = /\B@?/.source + '(?:' + /<([a-zA-Z][\w:]*)/.source + tagAttrs + /\s*>/.source + '(?:' + ( /[^<]/.source + '|' + // all tags that are not the start tag // eslint-disable-next-line regexp/strict /<\/?(?!\1\b)/.source + tagContent + '|' + // nested start tag nested( // eslint-disable-next-line regexp/strict /<\1/.source + tagAttrs + /\s*>/.source + '(?:' + ( /[^<]/.source + '|' + // all tags that are not the start tag // eslint-disable-next-line regexp/strict /<\/?(?!\1\b)/.source + tagContent + '|' + '<self>' ) + ')*' + // eslint-disable-next-line regexp/strict /<\/\1\s*>/.source, 2 ) ) + ')*' + // eslint-disable-next-line regexp/strict /<\/\1\s*>/.source + '|' + /</.source + tagContent + ')'; // Now for the actual language definition(s): // // Razor as a language has 2 parts: // 1) CSHTML: A markup-like language that has been extended with inline C# code expressions and blocks. // 2) C#+HTML: A variant of C# that can contain CSHTML tags as expressions. // // 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`. Prism.languages.cshtml = Prism.languages.extend('markup', {}); var csharpWithHtml = Prism.languages.insertBefore('csharp', 'string', { 'html': { pattern: RegExp(tagRegion), greedy: true, inside: Prism.languages.cshtml }, }, { csharp: Prism.languages.extend('csharp', {}) }); var cs = { pattern: /\S[\s\S]*/, alias: 'language-csharp', inside: csharpWithHtml }; var inlineValue = { pattern: RegExp(/(^|[^@])/.source + inlineCs), lookbehind: true, greedy: true, alias: 'variable', inside: { 'keyword': /^@/, 'csharp': cs } }; Prism.languages.cshtml.tag.pattern = RegExp(/<\/?/.source + tagContent); Prism.languages.cshtml.tag.inside['attr-value'].pattern = RegExp(/=\s*/.source + tagAttrValue); Prism.languages.insertBefore('inside', 'punctuation', { 'value': inlineValue }, Prism.languages.cshtml.tag.inside['attr-value']); Prism.languages.insertBefore('cshtml', '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 + '(?:' + /\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 } }, 'value': inlineValue, 'delegate-operator': { pattern: /(^|[^@])@(?=<)/, lookbehind: true, alias: 'operator' } }); Prism.languages.razor = Prism.languages.cshtml; }(Prism));