Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSHTML: Added support for @helper and inline C# inside attribute values #3355

Merged
merged 10 commits into from Mar 13, 2022
52 changes: 34 additions & 18 deletions components/prism-cshtml.js
Expand Up @@ -28,9 +28,15 @@
}

var round = nested(/\((?:[^()'"@/]|<str>|<comment>|<self>)*\)/.source, 2);
var square = 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(/<(?:[^<>'"@/]|<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
Expand All @@ -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: <p>That's it!</p>.

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*<tagAttrValue>|(?=[\s/>])))+)?/.source.replace(/<tagAttrValue>/, tagAttrValue);
var tagContent = /(?!\d)[^\s>\/=$<%]+/.source + tagAttrs + /\s*\/?>/.source;
var tagRegion =
/\B@?/.source +
Expand Down Expand Up @@ -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]*?\*@/,
Expand All @@ -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('|') +
')'
),
Expand All @@ -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: /(^|[^@])@(?=<)/,
Expand Down
2 changes: 1 addition & 1 deletion components/prism-cshtml.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions tests/languages/cshtml/block_feature.test
Expand Up @@ -94,6 +94,10 @@ finally
// Do critical section work
}

@helper TrialHelper(string name) {
// some code
}

----------------------------------------------------

[
Expand Down Expand Up @@ -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", "}"]
]]
]]
Expand Down
194 changes: 194 additions & 0 deletions tests/languages/cshtml/issue3354.test
@@ -0,0 +1,194 @@
<input type="text" placeholder="@Localize.GetLabelHtml("PLACEHOLDER")"/>

<h1>
@Localize.GetLabelHtml("TITLE")
</h1>

@{
var man = "Federico";
var text = string.Concat("Nice to meet you", " ", man);
}

@helper TrialHelper(string name) {
var text = string.Concat("Hello", " ", name);
<h1>
@(text + ", how's going?")
</h1>
<p>
Hello World!
</p>
}

----------------------------------------------------

[
["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", "}"]
]]
]]
]