diff --git a/components/prism-cshtml.js b/components/prism-cshtml.js
index a917626aa0..4643710d3d 100644
--- a/components/prism-cshtml.js
+++ b/components/prism-cshtml.js
@@ -28,9 +28,15 @@
}
var round = nested(/\((?:[^()'"@/]|||)*\)/.source, 2);
- var square = nested(/\[(?:[^\[\]'"@/]|||)*\]/.source, 2);
+ var square = nested(/\[(?:[^\[\]'"@/]|||)*\]/.source, 1);
var curly = nested(/\{(?:[^{}'"@/]|||)*\}/.source, 2);
- var angle = nested(/<(?:[^<>'"@/]|||)*>/.source, 2);
+ var angle = nested(/<(?:[^<>'"@/]||)*>/.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
@@ -44,7 +50,14 @@
// 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: That's it!
.
- var tagAttrs = /(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?/.source;
+ var tagAttrInlineCs = /@(?![\w()])/.source + '|' + inlineCs;
+ var tagAttrValue = '(?:' +
+ /"[^"@]*"|'[^'@]*'|[^\s'"@>=]+(?=[\s>])/.source +
+ '|' +
+ '["\'][^"\'@]*(?:(?:' + tagAttrInlineCs + ')[^"\'@]*)+["\']' +
+ ')';
+
+ var tagAttrs = /(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*|(?=[\s/>])))+)?/.source.replace(//, tagAttrValue);
var tagContent = /(?!\d)[^\s>\/=$<%]+/.source + tagAttrs + /\s*\/?>/.source;
var tagRegion =
/\B@?/.source +
@@ -110,6 +123,21 @@
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]*?\*@/,
@@ -134,6 +162,8 @@
/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('|') +
')'
),
@@ -155,21 +185,7 @@
}
},
- 'value': {
- pattern: RegExp(
- /(^|[^@])@/.source +
- /(?:await\b\s*)?/.source +
- '(?:' + /\w+\b/.source + '|' + round + ')' +
- '(?:' + /[?!]?\.\w+\b/.source + '|' + round + '|' + square + '|' + angle + round + ')*'
- ),
- lookbehind: true,
- greedy: true,
- alias: 'variable',
- inside: {
- 'keyword': /^@/,
- 'csharp': cs
- }
- },
+ 'value': inlineValue,
'delegate-operator': {
pattern: /(^|[^@])@(?=<)/,
diff --git a/components/prism-cshtml.min.js b/components/prism-cshtml.min.js
index 537152a87f..c5c96841e6 100644
--- a/components/prism-cshtml.min.js
+++ b/components/prism-cshtml.min.js
@@ -1 +1 @@
-!function(e){function s(e,s){for(var a=0;a/g,function(){return"(?:"+e+")"});return e.replace(//g,"[^\\s\\S]").replace(//g,'(?:@(?!")|"(?:[^\r\n\\\\"]|\\\\.)*"|@"(?:[^\\\\"]|""|\\\\[^])*"(?!")|'+"'(?:(?:[^\r\n'\\\\]|\\\\.|\\\\[Uux][\\da-fA-F]{1,8})'|(?=[^\\\\](?!'))))").replace(//g,"(?:/(?![/*])|//.*[\r\n]|/\\*[^*]*(?:\\*(?!/)[^*]*)*\\*/)")}var a=s("\\((?:[^()'\"@/]|||)*\\)",2),r=s("\\[(?:[^\\[\\]'\"@/]|||)*\\]",2),t=s("\\{(?:[^{}'\"@/]|||)*\\}",2),n=s("<(?:[^<>'\"@/]|||)*>",2),l="(?:\\s(?:\\s*[^\\s>/=]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))|(?=[\\s/>])))+)?",i="(?!\\d)[^\\s>/=$<%]+"+l+"\\s*/?>",o="\\B@?(?:<([a-zA-Z][\\w:]*)"+l+"\\s*>(?:[^<]|?(?!\\1\\b)"+i+"|"+s("<\\1"+l+"\\s*>(?:[^<]|?(?!\\1\\b)"+i+"|)*\\1\\s*>",2)+")*\\1\\s*>|<"+i+")";e.languages.cshtml=e.languages.extend("markup",{});var g={pattern:/\S[\s\S]*/,alias:"language-csharp",inside:e.languages.insertBefore("csharp","string",{html:{pattern:RegExp(o),greedy:!0,inside:e.languages.cshtml}},{csharp:e.languages.extend("csharp",{})})};e.languages.insertBefore("cshtml","prolog",{"razor-comment":{pattern:/@\*[\s\S]*?\*@/,greedy:!0,alias:"comment"},block:{pattern:RegExp("(^|[^@])@(?:"+[t,"(?:code|functions)\\s*"+t,"(?:for|foreach|lock|switch|using|while)\\s*"+a+"\\s*"+t,"do\\s*"+t+"\\s*while\\s*"+a+"(?:\\s*;)?","try\\s*"+t+"\\s*catch\\s*"+a+"\\s*"+t+"\\s*finally\\s*"+t,"if\\s*"+a+"\\s*"+t+"(?:\\s*else(?:\\s+if\\s*"+a+")?\\s*"+t+")*"].join("|")+")"),lookbehind:!0,greedy:!0,inside:{keyword:/^@\w*/,csharp:g}},directive:{pattern:/^([ \t]*)@(?:addTagHelper|attribute|implements|inherits|inject|layout|model|namespace|page|preservewhitespace|removeTagHelper|section|tagHelperPrefix|using)(?=\s).*/m,lookbehind:!0,greedy:!0,inside:{keyword:/^@\w+/,csharp:g}},value:{pattern:RegExp("(^|[^@])@(?:await\\b\\s*)?(?:\\w+\\b|"+a+")(?:[?!]?\\.\\w+\\b|"+a+"|"+r+"|"+n+a+")*"),lookbehind:!0,greedy:!0,alias:"variable",inside:{keyword:/^@/,csharp:g}},"delegate-operator":{pattern:/(^|[^@])@(?=<)/,lookbehind:!0,alias:"operator"}}),e.languages.razor=e.languages.cshtml}(Prism);
\ No newline at end of file
+!function(e){function s(e,s){for(var a=0;a/g,function(){return"(?:"+e+")"});return e.replace(//g,"[^\\s\\S]").replace(//g,'(?:@(?!")|"(?:[^\r\n\\\\"]|\\\\.)*"|@"(?:[^\\\\"]|""|\\\\[^])*"(?!")|'+"'(?:(?:[^\r\n'\\\\]|\\\\.|\\\\[Uux][\\da-fA-F]{1,8})'|(?=[^\\\\](?!'))))").replace(//g,"(?:/(?![/*])|//.*[\r\n]|/\\*[^*]*(?:\\*(?!/)[^*]*)*\\*/)")}var a=s("\\((?:[^()'\"@/]|||)*\\)",2),t=s("\\[(?:[^\\[\\]'\"@/]|||)*\\]",1),r=s("\\{(?:[^{}'\"@/]|||)*\\}",2),n="@(?:await\\b\\s*)?(?:(?!await\\b)\\w+\\b|"+a+")(?:[?!]?\\.\\w+\\b|(?:"+s("<(?:[^<>'\"@/]||)*>",1)+")?"+a+"|"+t+")*(?![?!\\.(\\[]|<(?!/))",l="(?:\"[^\"@]*\"|'[^'@]*'|[^\\s'\"@>=]+(?=[\\s>])|[\"'][^\"'@]*(?:(?:"+("@(?![\\w()])|"+n)+")[^\"'@]*)+[\"'])",i="(?:\\s(?:\\s*[^\\s>/=]+(?:\\s*=\\s*|(?=[\\s/>])))+)?".replace(//,l),g="(?!\\d)[^\\s>/=$<%]+"+i+"\\s*/?>",o="\\B@?(?:<([a-zA-Z][\\w:]*)"+i+"\\s*>(?:[^<]|?(?!\\1\\b)"+g+"|"+s("<\\1"+i+"\\s*>(?:[^<]|?(?!\\1\\b)"+g+"|)*\\1\\s*>",2)+")*\\1\\s*>|<"+g+")";e.languages.cshtml=e.languages.extend("markup",{});var c={pattern:/\S[\s\S]*/,alias:"language-csharp",inside:e.languages.insertBefore("csharp","string",{html:{pattern:RegExp(o),greedy:!0,inside:e.languages.cshtml}},{csharp:e.languages.extend("csharp",{})})},p={pattern:RegExp("(^|[^@])"+n),lookbehind:!0,greedy:!0,alias:"variable",inside:{keyword:/^@/,csharp:c}};e.languages.cshtml.tag.pattern=RegExp("?"+g),e.languages.cshtml.tag.inside["attr-value"].pattern=RegExp("=\\s*"+l),e.languages.insertBefore("inside","punctuation",{value:p},e.languages.cshtml.tag.inside["attr-value"]),e.languages.insertBefore("cshtml","prolog",{"razor-comment":{pattern:/@\*[\s\S]*?\*@/,greedy:!0,alias:"comment"},block:{pattern:RegExp("(^|[^@])@(?:"+[r,"(?:code|functions)\\s*"+r,"(?:for|foreach|lock|switch|using|while)\\s*"+a+"\\s*"+r,"do\\s*"+r+"\\s*while\\s*"+a+"(?:\\s*;)?","try\\s*"+r+"\\s*catch\\s*"+a+"\\s*"+r+"\\s*finally\\s*"+r,"if\\s*"+a+"\\s*"+r+"(?:\\s*else(?:\\s+if\\s*"+a+")?\\s*"+r+")*","helper\\s+\\w+\\s*"+a+"\\s*"+r].join("|")+")"),lookbehind:!0,greedy:!0,inside:{keyword:/^@\w*/,csharp:c}},directive:{pattern:/^([ \t]*)@(?:addTagHelper|attribute|implements|inherits|inject|layout|model|namespace|page|preservewhitespace|removeTagHelper|section|tagHelperPrefix|using)(?=\s).*/m,lookbehind:!0,greedy:!0,inside:{keyword:/^@\w+/,csharp:c}},value:p,"delegate-operator":{pattern:/(^|[^@])@(?=<)/,lookbehind:!0,alias:"operator"}}),e.languages.razor=e.languages.cshtml}(Prism);
\ No newline at end of file
diff --git a/tests/languages/cshtml/block_feature.test b/tests/languages/cshtml/block_feature.test
index 59bd8b05de..e3d1875316 100644
--- a/tests/languages/cshtml/block_feature.test
+++ b/tests/languages/cshtml/block_feature.test
@@ -94,6 +94,10 @@ finally
// Do critical section work
}
+@helper TrialHelper(string name) {
+ // some code
+}
+
----------------------------------------------------
[
@@ -893,6 +897,24 @@ finally
["punctuation", "("], "SomeLock", ["punctuation", ")"],
["punctuation", "{"],
["comment", "// Do critical section work"],
+ ["punctuation", "}"]
+ ]]
+ ]],
+
+ ["block", [
+ ["keyword", "@helper"],
+ ["csharp", [
+ ["function", "TrialHelper"],
+ ["punctuation", "("],
+ ["class-name", [
+ ["keyword", "string"]
+ ]],
+ " name",
+ ["punctuation", ")"],
+ ["punctuation", "{"],
+
+ ["comment", "// some code"],
+
["punctuation", "}"]
]]
]]
diff --git a/tests/languages/cshtml/issue3354.test b/tests/languages/cshtml/issue3354.test
new file mode 100644
index 0000000000..8b27f99005
--- /dev/null
+++ b/tests/languages/cshtml/issue3354.test
@@ -0,0 +1,194 @@
+
+
+
+ @Localize.GetLabelHtml("TITLE")
+
+
+@{
+ var man = "Federico";
+ var text = string.Concat("Nice to meet you", " ", man);
+}
+
+@helper TrialHelper(string name) {
+ var text = string.Concat("Hello", " ", name);
+
+ @(text + ", how's going?")
+
+
+ Hello World!
+
+}
+
+----------------------------------------------------
+
+[
+ ["tag", [
+ ["tag", [
+ ["punctuation", "<"],
+ "input"
+ ]],
+ ["attr-name", ["type"]],
+ ["attr-value", [
+ ["punctuation", "="],
+ ["punctuation", "\""],
+ "text",
+ ["punctuation", "\""]
+ ]],
+ ["attr-name", ["placeholder"]],
+ ["attr-value", [
+ ["punctuation", "="],
+ ["punctuation", "\""],
+ ["value", [
+ ["keyword", "@"],
+ ["csharp", [
+ "Localize",
+ ["punctuation", "."],
+ ["function", "GetLabelHtml"],
+ ["punctuation", "("],
+ ["string", "\"PLACEHOLDER\""],
+ ["punctuation", ")"]
+ ]]
+ ]],
+ ["punctuation", "\""]
+ ]],
+ ["punctuation", "/>"]
+ ]],
+
+ ["tag", [
+ ["tag", [
+ ["punctuation", "<"],
+ "h1"
+ ]],
+ ["punctuation", ">"]
+ ]],
+ ["value", [
+ ["keyword", "@"],
+ ["csharp", [
+ "Localize",
+ ["punctuation", "."],
+ ["function", "GetLabelHtml"],
+ ["punctuation", "("],
+ ["string", "\"TITLE\""],
+ ["punctuation", ")"]
+ ]]
+ ]],
+ ["tag", [
+ ["tag", [
+ ["punctuation", ""],
+ "h1"
+ ]],
+ ["punctuation", ">"]
+ ]],
+
+ ["block", [
+ ["keyword", "@"],
+ ["csharp", [
+ ["punctuation", "{"],
+
+ ["class-name", [
+ ["keyword", "var"]
+ ]],
+ " man ",
+ ["operator", "="],
+ ["string", "\"Federico\""],
+ ["punctuation", ";"],
+
+ ["class-name", [
+ ["keyword", "var"]
+ ]],
+ " text ",
+ ["operator", "="],
+ ["keyword", "string"],
+ ["punctuation", "."],
+ ["function", "Concat"],
+ ["punctuation", "("],
+ ["string", "\"Nice to meet you\""],
+ ["punctuation", ","],
+ ["string", "\" \""],
+ ["punctuation", ","],
+ " man",
+ ["punctuation", ")"],
+ ["punctuation", ";"],
+
+ ["punctuation", "}"]
+ ]]
+ ]],
+
+ ["block", [
+ ["keyword", "@helper"],
+ ["csharp", [
+ ["function", "TrialHelper"],
+ ["punctuation", "("],
+ ["class-name", [
+ ["keyword", "string"]
+ ]],
+ " name",
+ ["punctuation", ")"],
+ ["punctuation", "{"],
+
+ ["class-name", [
+ ["keyword", "var"]
+ ]],
+ " text ",
+ ["operator", "="],
+ ["keyword", "string"],
+ ["punctuation", "."],
+ ["function", "Concat"],
+ ["punctuation", "("],
+ ["string", "\"Hello\""],
+ ["punctuation", ","],
+ ["string", "\" \""],
+ ["punctuation", ","],
+ " name",
+ ["punctuation", ")"],
+ ["punctuation", ";"],
+
+ ["html", [
+ ["tag", [
+ ["tag", [
+ ["punctuation", "<"],
+ "h1"
+ ]],
+ ["punctuation", ">"]
+ ]],
+ ["value", [
+ ["keyword", "@"],
+ ["csharp", [
+ ["punctuation", "("],
+ "text ",
+ ["operator", "+"],
+ ["string", "\", how's going?\""],
+ ["punctuation", ")"]
+ ]]
+ ]],
+ ["tag", [
+ ["tag", [
+ ["punctuation", ""],
+ "h1"
+ ]],
+ ["punctuation", ">"]
+ ]]
+ ]],
+
+ ["html", [
+ ["tag", [
+ ["tag", [
+ ["punctuation", "<"],
+ "p"
+ ]],
+ ["punctuation", ">"]
+ ]],
+ "\r\n Hello World!\r\n ",
+ ["tag", [
+ ["tag", [
+ ["punctuation", ""],
+ "p"
+ ]],
+ ["punctuation", ">"]
+ ]]
+ ]],
+
+ ["punctuation", "}"]
+ ]]
+ ]]
+]