Skip to content

Commit df4e367

Browse files
keith-hallmichaelblyons
authored andcommitted
Initial work on a Razor syntax (#6)
* Initial work on a Razor syntax * Use kebab case for contexts in the razor syntax * Use more realistic example for "using" syntax test * add missing text.html scope, clear text.html scope when inside C# code
1 parent 5e20b6a commit df4e367

2 files changed

Lines changed: 356 additions & 0 deletions

File tree

razor.sublime-syntax

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
%YAML 1.2
2+
---
3+
# http://www.sublimetext.com/docs/3/syntax.html
4+
name: HTML Razor (C#)
5+
file_extensions:
6+
- cshtml
7+
scope: embedding.cshtml.razor
8+
contexts:
9+
main:
10+
- include: html
11+
12+
html:
13+
- match: ''
14+
set: razor-html
15+
16+
razor-comments:
17+
- match: '@\*'
18+
scope: punctuation.definition.comment.begin.cshtml
19+
push:
20+
- meta_scope: comment.block.cshtml
21+
- match: '\*@'
22+
scope: punctuation.definition.comment.end.cshtml
23+
pop: true
24+
25+
razor:
26+
- include: razor-comments
27+
- match: (@)(model)\b
28+
captures:
29+
1: punctuation.section.embedded.line.cshtml
30+
2: source.cs.embedded.html keyword.other.cshtml
31+
embed: scope:source.cs
32+
embed_scope: source.cs.embedded.html
33+
escape: $
34+
- match: (@)(section)\b(?:\s+(\w+))?
35+
captures:
36+
1: punctuation.section.embedded.line.cshtml
37+
2: source.cs.embedded.html keyword.other.cshtml
38+
3: entity.name.section.cshtml
39+
push:
40+
- match: \{
41+
scope: punctuation.section.block.begin.cshtml
42+
set:
43+
- match: \}
44+
scope: punctuation.section.block.end.cshtml
45+
pop: true
46+
- include: razor-html
47+
- match: (@)(functions)\b
48+
captures:
49+
1: punctuation.section.embedded.line.cshtml
50+
2: source.cs.embedded.html keyword.other.cshtml
51+
push:
52+
- match: \{
53+
scope: punctuation.section.block.begin.cshtml
54+
set:
55+
- clear_scopes: 1
56+
- meta_scope: source.cs.embedded.functions.cshtml
57+
- match: \}
58+
scope: punctuation.section.block.end.cshtml
59+
pop: true
60+
- include: scope:source.cs#code_block_in
61+
- match: \s*(?!\{)(?=\S)
62+
scope: invalid.illegal.expected-block.cshtml
63+
pop: true
64+
- match: (@)(using)\b(?!\s*\()
65+
captures:
66+
1: punctuation.section.embedded.line.cshtml
67+
2: source.cs.embedded.html keyword.other.cshtml
68+
embed: scope:source.cs
69+
embed_scope: source.cs.embedded.html
70+
escape: $
71+
- match: '@@'
72+
scope: constant.character.escape.cshtml
73+
- match: (@)(?=(?:if|switch|for|foreach|using|try|lock)\b)
74+
captures:
75+
1: punctuation.section.embedded.cshtml
76+
push:
77+
- clear_scopes: 1
78+
- meta_content_scope: source.cs.embedded.html
79+
- include: scope:source.cs#line_of_code
80+
- match: ''
81+
pop: true
82+
with_prototype:
83+
- include: razor-block-prototype
84+
- match: '[\w-+]+@\w+' # ignore email addresses
85+
- match: '@(?=(?:\w+\.)*\w+(?:[<\s]|$))'
86+
scope: punctuation.section.embedded.line.cshtml
87+
embed: scope:source.cs#line_of_code_in
88+
embed_scope: source.cs.embedded.html
89+
escape: $|(?=[<\s])
90+
- match: '@(?=(?:\w+\.)*\w+\()'
91+
scope: punctuation.section.embedded.line.cshtml
92+
push:
93+
- meta_content_scope: source.cs.embedded.html
94+
- match: (?=[<\s])
95+
pop: true
96+
- include: scope:source.cs#line_of_code_in
97+
- match: '@\('
98+
scope: punctuation.section.embedded.begin.cshtml
99+
push:
100+
- clear_scopes: 1
101+
- meta_content_scope: source.cs.embedded.html
102+
- match: \)
103+
scope: punctuation.section.embedded.end.cshtml
104+
pop: true
105+
- include: scope:source.cs#line_of_code_in
106+
#- match: '@(?=\w+(?:\.\w+)*)(?!\s*[+=|(])' # TODO: support a.b[2] etc.
107+
# comment: property/variable access
108+
# scope: punctuation.section.embedded.line.cshtml
109+
# #push: razor-block # TODO: stop at closing " etc.
110+
- match: '@'
111+
scope: punctuation.section.embedded.line.cshtml
112+
push: razor-block
113+
114+
razor-block-prototype:
115+
- match: (?=[<&])
116+
embed: scope:text.html.basic
117+
escape: (?=[@}]|$)
118+
- match: '@:'
119+
scope: punctuation.section.embedded.html.cshtml
120+
embed: scope:text.html.basic
121+
escape: $
122+
123+
eat-whitespace:
124+
- match: (?=\S)
125+
pop: true
126+
127+
razor-block:
128+
- clear_scopes: 1
129+
- meta_content_scope: source.cs.embedded.html
130+
- match: (?=[<\s])
131+
pop: true
132+
- match: (?=\S)(?!@)
133+
push:
134+
- include: scope:source.cs#line_of_code
135+
with_prototype:
136+
- include: razor-block-prototype
137+
138+
razor-html:
139+
- meta_content_scope: text.html.basic
140+
- include: razor
141+
- include: scope:text.html.basic

tests/syntax_test_cshtml.cshtml

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
<!-- SYNTAX TEST "Packages/HTML (C#)/razor.sublime-syntax" -->
2+
<!-- https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-2.1 -->
3+
@using System.Collections.Generic
4+
<!-- <- punctuation.section.embedded.line - source.cs.embedded -->
5+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.cs.embedded -->
6+
<!-- ^ keyword.other -->
7+
<!-- ^ - source.cs.embedded -->
8+
@model IEnumerable<SomeNamespace.SomeType>
9+
<!-- <- punctuation.section.embedded.line - source.cs.embedded -->
10+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.cs.embedded -->
11+
<!-- ^ keyword.other -->
12+
<!-- ^^^^^^^^^^^ support.type -->
13+
<!-- ^ punctuation.definition.generic.begin -->
14+
<!-- ^^^^^^^^^^^^^ support.type -->
15+
<!-- ^ punctuation.accessor.dot.namespace -->
16+
<!-- ^^^^^^^^ support.type -->
17+
<!-- ^ punctuation.definition.generic.end -->
18+
<!-- ^ - source.cs.embedded -->
19+
20+
<p>@@Username</p>
21+
<!-- ^^^ text.html meta.tag.block.any -->
22+
<!-- ^^ constant.character.escape -->
23+
<!-- ^^^^^^^^^^^^^ text.html -->
24+
<!-- ^^^^ meta.tag.block.any -->
25+
26+
<p>@Username</p>
27+
<!-- ^^^ meta.tag.block.any -->
28+
<!-- ^ punctuation.section.embedded.line -->
29+
<!-- ^^^^^^^^ source.cs.embedded variable.other -->
30+
<!-- ^^^^ meta.tag.block.any -->
31+
32+
<a href="mailto:Support@contoso.com">Support@contoso.com</a>
33+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.tag.inline.a - source.cs -->
34+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^ - source.cs -->
35+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ text.html -->
36+
<!-- ^^^^ meta.tag.inline.a -->
37+
38+
<p>@DateTime.Now</p>
39+
<!-- ^^^^^^^ source.cs.embedded variable.other -->
40+
<!-- ^ source.cs.embedded punctuation.accessor.dot -->
41+
<!-- ^^^ source.cs.embedded variable.other -->
42+
<!-- ^^^ meta.tag.block.any -->
43+
<p>@DateTime.IsLeapYear(2016)</p>
44+
<!-- ^^^^^^^ source.cs.embedded variable.other -->
45+
<!-- ^ source.cs.embedded punctuation.accessor.dot -->
46+
<!-- ^^^^^^^^^^ source.cs.embedded meta.function-call variable.function -->
47+
<!-- ^ punctuation.section.group.begin -->
48+
<!-- ^^^^ constant.numeric.integer.decimal -->
49+
<!-- ^ punctuation.section.group.end -->
50+
<!-- ^^^ meta.tag.block.any -->
51+
52+
<p>@(GenericMethod<int>())</p>
53+
<!-- ^^ punctuation.section.embedded.begin -->
54+
<!-- ^^^^^^^^^^^^^^^^^^^^ source.cs.embedded - text.html -->
55+
<!-- ^^^^^^^^^^^^^ meta.function-call variable.function -->
56+
<!-- ^ punctuation.definition.generic.begin -->
57+
<!-- ^^^ storage.type -->
58+
<!-- ^ punctuation.definition.generic.end -->
59+
<!-- ^ punctuation.section.group.begin -->
60+
<!-- ^ punctuation.section.group.end -->
61+
<!-- ^ punctuation.section.embedded.end -->
62+
<!-- ^^^ meta.tag.block.any -->
63+
64+
@("<span>Hello World</span>")
65+
<!-- <- punctuation.section.embedded.begin -->
66+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^ source.cs string.quoted.double - text.html -->
67+
<!-- ^ punctuation.section.embedded.end -->
68+
@Html.Raw("<span>Hello World</span>")
69+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call -->
70+
<!-- ^^^ variable.function -->
71+
<!-- <- punctuation.section.embedded.line -->
72+
<!-- ^^^^^^^^^^^^^^^^^^^^^^^ string.quoted.double -->
73+
<!-- ^ punctuation.section.group.end -->
74+
75+
@if (value % 2 == 0)
76+
<!-- ^ punctuation.section.embedded - source.cs -->
77+
<!-- ^^ source.cs keyword.control.conditional.if - text.html -->
78+
{
79+
<!-- <- meta.block punctuation.section.block.begin -->
80+
<p>The value was even.</p>
81+
<!--^^^ meta.tag.block.any -->
82+
}
83+
else if (value >= 1337)
84+
<!-- <- keyword.control.conditional.elseif -->
85+
{
86+
<p>The value is large.</p>
87+
}
88+
else
89+
<!-- <- keyword.control.conditional.else -->
90+
{
91+
<p>The value is odd and small.</p>
92+
}
93+
94+
@switch (value)
95+
{
96+
case 1:
97+
<!--^^^^ keyword.control.switch.case -->
98+
<p>The value is 1!</p>
99+
break;
100+
case 1337:
101+
<p>Your number is 1337!</p>
102+
break;
103+
default:
104+
<p>Your number wasn't 1 or 1337.</p>
105+
break;
106+
<!-- ^^^^^ keyword.control.flow.break -->
107+
}
108+
@*
109+
@for (var i = 0; i < people.Length; i++)
110+
<!-- <- comment.block - punctuation -->
111+
{
112+
var person = people[i];
113+
<text>Name: @person.Name</text>
114+
}
115+
116+
@for (var i = 0; i < people.Length; i++)
117+
{
118+
var person = people[i];
119+
@:Name: @person.Name
120+
} *@
121+
<!-- ^^ comment.block punctuation.definition.comment.end -->
122+
123+
@{
124+
var quote = "The future depends on what you do today. - Mahatma Gandhi";
125+
<!-- ^^ source.cs.embedded - text.html -->
126+
}
127+
<!-- ^ text.html - source -->
128+
129+
<p>@quote</p>
130+
131+
@{
132+
quote = "Hate cannot drive out hate, only love can do that. - Martin Luther King, Jr.";
133+
}
134+
135+
<p>@quote</p>
136+
137+
@using (Html.BeginForm())
138+
{
139+
<div>
140+
email:
141+
<!-- TODO: what should the above be scoped as? -->
142+
<input type="email" id="Email" value="">
143+
<button>Register</button>
144+
</div>
145+
}
146+
147+
@try
148+
{
149+
throw new InvalidOperationException("You did something invalid.");
150+
}
151+
catch (Exception ex)
152+
{
153+
<p>The exception message: @ex.Message</p>
154+
}
155+
finally
156+
{
157+
<p>The finally statement.</p>
158+
}
159+
160+
@lock (SomeLock)
161+
{
162+
// Do critical section work
163+
}
164+
165+
@*
166+
@{
167+
/* C# comment */
168+
// Another C# comment
169+
}
170+
<!-- HTML comment -->
171+
*@
172+
173+
@functions {
174+
// <!-- ^^ keyword.other -->
175+
public string GetHello()
176+
// <!-- ^^^^^^^^^^^^^^^^^^^^^ source.cs.embedded.functions -->
177+
// <!-- ^^ storage.modifier.access -->
178+
{
179+
return "Hello";
180+
}
181+
}
182+
<!-- ^ - source.cs.embedded.functions -->
183+
184+
<div>From method: @GetHello()</div>
185+
186+
<form id="searchForm" action="@Url.Action("Search", "Controller")" method="post">
187+
<!-- TODO: scope @ constructs in HTML attribute values correctly -->
188+
<div style="@(HtmlHelpers.DevModeEnabled(Request) ? "display: none;" : "")">
189+
</div>
190+
</form>
191+
192+
<article data-example="@example.ID">
193+
</article>
194+
195+
@foreach (var item in Model)
196+
{
197+
if (item.ShouldBeDisplayed)
198+
{
199+
<article data-id="@item.ID">
200+
<!-- TODO: scope at constructs in HTML attribute values correctly even when inside a razor code block -->
201+
</article>
202+
}
203+
}
204+
205+
@section Scripts {
206+
<!-- ^^^^^^^ entity.name.section -->
207+
<script type="text/javascript" src="/scripts/main.js"></script>
208+
<!-- ^^^^^^ entity.name.tag.script -->
209+
210+
<script type="text/javascript">
211+
function find() {
212+
var val = $('@HtmlHelpers.Something').val();
213+
}
214+
</script>
215+
}

0 commit comments

Comments
 (0)