prism-cshtml.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Docs:
  2. // https://docs.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-5.0&tabs=visual-studio
  3. // https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0
  4. (function (Prism) {
  5. var commentLike = /\/(?![/*])|\/\/.*[\r\n]|\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\//.source;
  6. var stringLike =
  7. /@(?!")|"(?:[^\r\n\\"]|\\.)*"|@"(?:[^\\"]|""|\\[\s\S])*"(?!")/.source +
  8. '|' +
  9. /'(?:(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'|(?=[^\\](?!')))/.source;
  10. /**
  11. * Creates a nested pattern where all occurrences of the string `<<self>>` are replaced with the pattern itself.
  12. *
  13. * @param {string} pattern
  14. * @param {number} depthLog2
  15. * @returns {string}
  16. */
  17. function nested(pattern, depthLog2) {
  18. for (var i = 0; i < depthLog2; i++) {
  19. pattern = pattern.replace(/<self>/g, function () { return '(?:' + pattern + ')'; });
  20. }
  21. return pattern
  22. .replace(/<self>/g, '[^\\s\\S]')
  23. .replace(/<str>/g, '(?:' + stringLike + ')')
  24. .replace(/<comment>/g, '(?:' + commentLike + ')');
  25. }
  26. var round = nested(/\((?:[^()'"@/]|<str>|<comment>|<self>)*\)/.source, 2);
  27. var square = nested(/\[(?:[^\[\]'"@/]|<str>|<comment>|<self>)*\]/.source, 1);
  28. var curly = nested(/\{(?:[^{}'"@/]|<str>|<comment>|<self>)*\}/.source, 2);
  29. var angle = nested(/<(?:[^<>'"@/]|<comment>|<self>)*>/.source, 1);
  30. var inlineCs = /@/.source +
  31. /(?:await\b\s*)?/.source +
  32. '(?:' + /(?!await\b)\w+\b/.source + '|' + round + ')' +
  33. '(?:' + /[?!]?\.\w+\b/.source + '|' + '(?:' + angle + ')?' + round + '|' + square + ')*' +
  34. /(?![?!\.(\[]|<(?!\/))/.source;
  35. // Note about the above bracket patterns:
  36. // They all ignore HTML expressions that might be in the C# code. This is a problem because HTML (like strings and
  37. // comments) is parsed differently. This is a huge problem because HTML might contain brackets and quotes which
  38. // messes up the bracket and string counting implemented by the above patterns.
  39. //
  40. // This problem is not fixable because 1) HTML expression are highly context sensitive and very difficult to detect
  41. // and 2) they require one capturing group at every nested level. See the `tagRegion` pattern to admire the
  42. // complexity of an HTML expression.
  43. //
  44. // To somewhat alleviate the problem a bit, the patterns for characters (e.g. 'a') is very permissive, it also
  45. // allows invalid characters to support HTML expressions like this: <p>That's it!</p>.
  46. var tagAttrInlineCs = /@(?![\w()])/.source + '|' + inlineCs;
  47. var tagAttrValue = '(?:' +
  48. /"[^"@]*"|'[^'@]*'|[^\s'"@>=]+(?=[\s>])/.source +
  49. '|' +
  50. '["\'][^"\'@]*(?:(?:' + tagAttrInlineCs + ')[^"\'@]*)+["\']' +
  51. ')';
  52. var tagAttrs = /(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*<tagAttrValue>|(?=[\s/>])))+)?/.source.replace(/<tagAttrValue>/, tagAttrValue);
  53. var tagContent = /(?!\d)[^\s>\/=$<%]+/.source + tagAttrs + /\s*\/?>/.source;
  54. var tagRegion =
  55. /\B@?/.source +
  56. '(?:' +
  57. /<([a-zA-Z][\w:]*)/.source + tagAttrs + /\s*>/.source +
  58. '(?:' +
  59. (
  60. /[^<]/.source +
  61. '|' +
  62. // all tags that are not the start tag
  63. // eslint-disable-next-line regexp/strict
  64. /<\/?(?!\1\b)/.source + tagContent +
  65. '|' +
  66. // nested start tag
  67. nested(
  68. // eslint-disable-next-line regexp/strict
  69. /<\1/.source + tagAttrs + /\s*>/.source +
  70. '(?:' +
  71. (
  72. /[^<]/.source +
  73. '|' +
  74. // all tags that are not the start tag
  75. // eslint-disable-next-line regexp/strict
  76. /<\/?(?!\1\b)/.source + tagContent +
  77. '|' +
  78. '<self>'
  79. ) +
  80. ')*' +
  81. // eslint-disable-next-line regexp/strict
  82. /<\/\1\s*>/.source,
  83. 2
  84. )
  85. ) +
  86. ')*' +
  87. // eslint-disable-next-line regexp/strict
  88. /<\/\1\s*>/.source +
  89. '|' +
  90. /</.source + tagContent +
  91. ')';
  92. // Now for the actual language definition(s):
  93. //
  94. // Razor as a language has 2 parts:
  95. // 1) CSHTML: A markup-like language that has been extended with inline C# code expressions and blocks.
  96. // 2) C#+HTML: A variant of C# that can contain CSHTML tags as expressions.
  97. //
  98. // In the below code, both CSHTML and C#+HTML will be create as separate language definitions that reference each
  99. // other. However, only CSHTML will be exported via `Prism.languages`.
  100. Prism.languages.cshtml = Prism.languages.extend('markup', {});
  101. var csharpWithHtml = Prism.languages.insertBefore('csharp', 'string', {
  102. 'html': {
  103. pattern: RegExp(tagRegion),
  104. greedy: true,
  105. inside: Prism.languages.cshtml
  106. },
  107. }, { csharp: Prism.languages.extend('csharp', {}) });
  108. var cs = {
  109. pattern: /\S[\s\S]*/,
  110. alias: 'language-csharp',
  111. inside: csharpWithHtml
  112. };
  113. var inlineValue = {
  114. pattern: RegExp(/(^|[^@])/.source + inlineCs),
  115. lookbehind: true,
  116. greedy: true,
  117. alias: 'variable',
  118. inside: {
  119. 'keyword': /^@/,
  120. 'csharp': cs
  121. }
  122. };
  123. Prism.languages.cshtml.tag.pattern = RegExp(/<\/?/.source + tagContent);
  124. Prism.languages.cshtml.tag.inside['attr-value'].pattern = RegExp(/=\s*/.source + tagAttrValue);
  125. Prism.languages.insertBefore('inside', 'punctuation', { 'value': inlineValue }, Prism.languages.cshtml.tag.inside['attr-value']);
  126. Prism.languages.insertBefore('cshtml', 'prolog', {
  127. 'razor-comment': {
  128. pattern: /@\*[\s\S]*?\*@/,
  129. greedy: true,
  130. alias: 'comment'
  131. },
  132. 'block': {
  133. pattern: RegExp(
  134. /(^|[^@])@/.source +
  135. '(?:' +
  136. [
  137. // @{ ... }
  138. curly,
  139. // @code{ ... }
  140. /(?:code|functions)\s*/.source + curly,
  141. // @for (...) { ... }
  142. /(?:for|foreach|lock|switch|using|while)\s*/.source + round + /\s*/.source + curly,
  143. // @do { ... } while (...);
  144. /do\s*/.source + curly + /\s*while\s*/.source + round + /(?:\s*;)?/.source,
  145. // @try { ... } catch (...) { ... } finally { ... }
  146. /try\s*/.source + curly + /\s*catch\s*/.source + round + /\s*/.source + curly + /\s*finally\s*/.source + curly,
  147. // @if (...) {...} else if (...) {...} else {...}
  148. /if\s*/.source + round + /\s*/.source + curly + '(?:' + /\s*else/.source + '(?:' + /\s+if\s*/.source + round + ')?' + /\s*/.source + curly + ')*',
  149. // @helper Ident(params) { ... }
  150. /helper\s+\w+\s*/.source + round + /\s*/.source + curly,
  151. ].join('|') +
  152. ')'
  153. ),
  154. lookbehind: true,
  155. greedy: true,
  156. inside: {
  157. 'keyword': /^@\w*/,
  158. 'csharp': cs
  159. }
  160. },
  161. 'directive': {
  162. pattern: /^([ \t]*)@(?:addTagHelper|attribute|implements|inherits|inject|layout|model|namespace|page|preservewhitespace|removeTagHelper|section|tagHelperPrefix|using)(?=\s).*/m,
  163. lookbehind: true,
  164. greedy: true,
  165. inside: {
  166. 'keyword': /^@\w+/,
  167. 'csharp': cs
  168. }
  169. },
  170. 'value': inlineValue,
  171. 'delegate-operator': {
  172. pattern: /(^|[^@])@(?=<)/,
  173. lookbehind: true,
  174. alias: 'operator'
  175. }
  176. });
  177. Prism.languages.razor = Prism.languages.cshtml;
  178. }(Prism));