Skip to content
This repository was archived by the owner on Dec 19, 2018. It is now read-only.

Commit eea6044

Browse files
committed
Razor parser rewrite
- Rewrite CSharp parser - Basic rewrite of HTML parser - Define and generate syntax nodes and boilerplate - Rewrite ClassifiedSpan generation logic - Update parser test infrastructure
1 parent d2d84d0 commit eea6044

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+10588
-800
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using Microsoft.AspNetCore.Razor.Language.Legacy;
8+
using Microsoft.AspNetCore.Razor.Language.Syntax;
9+
10+
namespace Microsoft.AspNetCore.Razor.Language
11+
{
12+
internal class ClassifiedSpanVisitor : SyntaxRewriter
13+
{
14+
private RazorSourceDocument _source;
15+
private List<ClassifiedSpanInternal> _spans;
16+
private BlockKindInternal _currentBlockKind;
17+
private SyntaxNode _currentBlock;
18+
19+
public ClassifiedSpanVisitor(RazorSourceDocument source)
20+
{
21+
_source = source;
22+
_spans = new List<ClassifiedSpanInternal>();
23+
_currentBlockKind = BlockKindInternal.Markup;
24+
}
25+
26+
public IReadOnlyList<ClassifiedSpanInternal> ClassifiedSpans => _spans;
27+
28+
public override SyntaxNode VisitRazorCommentBlock(RazorCommentBlockSyntax node)
29+
{
30+
return WriteBlock(node, BlockKindInternal.Comment, base.VisitRazorCommentBlock);
31+
}
32+
33+
public override SyntaxNode VisitCSharpCodeBlock(CSharpCodeBlockSyntax node)
34+
{
35+
if (node.Parent is CSharpStatementBodySyntax ||
36+
node.Parent is CSharpExpressionBodySyntax ||
37+
node.Parent is CSharpImplicitExpressionBodySyntax ||
38+
node.Parent is CSharpDirectiveBodySyntax)
39+
{
40+
return base.VisitCSharpCodeBlock(node);
41+
}
42+
43+
return WriteBlock(node, BlockKindInternal.Statement, base.VisitCSharpCodeBlock);
44+
}
45+
46+
public override SyntaxNode VisitCSharpStatement(CSharpStatement node)
47+
{
48+
return WriteBlock(node, BlockKindInternal.Statement, base.VisitCSharpStatement);
49+
}
50+
51+
public override SyntaxNode VisitCSharpExpression(CSharpExpression node)
52+
{
53+
return WriteBlock(node, BlockKindInternal.Expression, base.VisitCSharpExpression);
54+
}
55+
56+
public override SyntaxNode VisitCSharpImplicitExpression(CSharpImplicitExpression node)
57+
{
58+
return WriteBlock(node, BlockKindInternal.Expression, base.VisitCSharpImplicitExpression);
59+
}
60+
61+
public override SyntaxNode VisitCSharpDirective(CSharpDirectiveSyntax node)
62+
{
63+
return WriteBlock(node, BlockKindInternal.Directive, base.VisitCSharpDirective);
64+
}
65+
66+
public override SyntaxNode VisitCSharpTemplateBlock(CSharpTemplateBlockSyntax node)
67+
{
68+
return WriteBlock(node, BlockKindInternal.Template, base.VisitCSharpTemplateBlock);
69+
}
70+
71+
public override SyntaxNode VisitHtmlMarkupBlock(HtmlMarkupBlockSyntax node)
72+
{
73+
return WriteBlock(node, BlockKindInternal.Markup, base.VisitHtmlMarkupBlock);
74+
}
75+
76+
public override SyntaxNode VisitHtmlTagBlock(HtmlTagBlockSyntax node)
77+
{
78+
return WriteBlock(node, BlockKindInternal.Tag, base.VisitHtmlTagBlock);
79+
}
80+
81+
public override SyntaxNode VisitHtmlAttributeBlock(HtmlAttributeBlockSyntax node)
82+
{
83+
return WriteBlock(node, BlockKindInternal.Markup, n =>
84+
{
85+
var equalsSyntax = SyntaxFactory.HtmlTextLiteral(new SyntaxList<SyntaxToken>(node.EqualsToken));
86+
var mergedAttributePrefix = MergeTextLiteralSpans(node.NamePrefix, node.Name, node.NameSuffix, equalsSyntax, node.ValuePrefix);
87+
Visit(mergedAttributePrefix);
88+
Visit(node.Value);
89+
Visit(node.ValueSuffix);
90+
91+
return n;
92+
});
93+
}
94+
95+
public override SyntaxNode VisitHtmlMinimizedAttributeBlock(HtmlMinimizedAttributeBlockSyntax node)
96+
{
97+
return WriteBlock(node, BlockKindInternal.Markup, n =>
98+
{
99+
var mergedAttributePrefix = MergeTextLiteralSpans(node.NamePrefix, node.Name);
100+
Visit(mergedAttributePrefix);
101+
102+
return n;
103+
});
104+
}
105+
106+
public override SyntaxNode VisitHtmlCommentBlock(HtmlCommentBlockSyntax node)
107+
{
108+
return WriteBlock(node, BlockKindInternal.HtmlComment, base.VisitHtmlCommentBlock);
109+
}
110+
111+
public override SyntaxNode VisitHtmlDynamicAttributeValue(HtmlDynamicAttributeValueSyntax node)
112+
{
113+
return WriteBlock(node, BlockKindInternal.Markup, base.VisitHtmlDynamicAttributeValue);
114+
}
115+
116+
public override SyntaxNode VisitRazorMetaCode(RazorMetaCodeSyntax node)
117+
{
118+
WriteSpan(node, SpanKindInternal.MetaCode);
119+
return base.VisitRazorMetaCode(node);
120+
}
121+
122+
public override SyntaxNode VisitCSharpTransition(CSharpTransitionSyntax node)
123+
{
124+
WriteSpan(node, SpanKindInternal.Transition);
125+
return base.VisitCSharpTransition(node);
126+
}
127+
128+
public override SyntaxNode VisitHtmlTransition(HtmlTransitionSyntax node)
129+
{
130+
WriteSpan(node, SpanKindInternal.Transition);
131+
return base.VisitHtmlTransition(node);
132+
}
133+
134+
public override SyntaxNode VisitCSharpStatementLiteral(CSharpStatementLiteralSyntax node)
135+
{
136+
WriteSpan(node, SpanKindInternal.Code);
137+
return base.VisitCSharpStatementLiteral(node);
138+
}
139+
140+
public override SyntaxNode VisitCSharpExpressionLiteral(CSharpExpressionLiteralSyntax node)
141+
{
142+
WriteSpan(node, SpanKindInternal.Code);
143+
return base.VisitCSharpExpressionLiteral(node);
144+
}
145+
146+
public override SyntaxNode VisitCSharpHiddenLiteral(CSharpHiddenLiteralSyntax node)
147+
{
148+
WriteSpan(node, SpanKindInternal.Code);
149+
return base.VisitCSharpHiddenLiteral(node);
150+
}
151+
152+
public override SyntaxNode VisitCSharpNoneLiteral(CSharpNoneLiteralSyntax node)
153+
{
154+
WriteSpan(node, SpanKindInternal.None);
155+
return base.VisitCSharpNoneLiteral(node);
156+
}
157+
158+
public override SyntaxNode VisitHtmlLiteralAttributeValue(HtmlLiteralAttributeValueSyntax node)
159+
{
160+
WriteSpan(node, SpanKindInternal.Markup);
161+
return base.VisitHtmlLiteralAttributeValue(node);
162+
}
163+
164+
public override SyntaxNode VisitHtmlTextLiteral(HtmlTextLiteralSyntax node)
165+
{
166+
if (node.Parent is HtmlLiteralAttributeValueSyntax)
167+
{
168+
return base.VisitHtmlTextLiteral(node);
169+
}
170+
171+
WriteSpan(node, SpanKindInternal.Markup);
172+
return base.VisitHtmlTextLiteral(node);
173+
}
174+
175+
private SyntaxNode WriteBlock<TNode>(TNode node, BlockKindInternal kind, Func<TNode, SyntaxNode> handler) where TNode : SyntaxNode
176+
{
177+
var previousBlock = _currentBlock;
178+
var previousKind = _currentBlockKind;
179+
180+
_currentBlock = node;
181+
_currentBlockKind = kind;
182+
183+
var result = handler(node);
184+
185+
_currentBlock = previousBlock;
186+
_currentBlockKind = previousKind;
187+
188+
return result;
189+
}
190+
191+
private void WriteSpan(SyntaxNode node, SpanKindInternal kind)
192+
{
193+
if (node.IsMissing)
194+
{
195+
return;
196+
}
197+
198+
var spanSource = GetSourceSpanForNode(node);
199+
var blockSource = GetSourceSpanForNode(_currentBlock);
200+
var acceptedCharacters = AcceptedCharactersInternal.Any;
201+
var annotation = node.GetAnnotationValue(SyntaxConstants.SpanContextKind);
202+
if (annotation is SpanContext context)
203+
{
204+
acceptedCharacters = context.EditHandler.AcceptedCharacters;
205+
}
206+
207+
var span = new ClassifiedSpanInternal(spanSource, blockSource, kind, _currentBlockKind, acceptedCharacters);
208+
_spans.Add(span);
209+
}
210+
211+
private HtmlTextLiteralSyntax MergeTextLiteralSpans(params HtmlTextLiteralSyntax[] literalSyntaxes)
212+
{
213+
if (literalSyntaxes == null || literalSyntaxes.Length == 0)
214+
{
215+
return null;
216+
}
217+
218+
SyntaxNode parent = null;
219+
var position = 0;
220+
var seenFirstLiteral = false;
221+
var builder = Syntax.InternalSyntax.SyntaxListBuilder.Create();
222+
223+
foreach (var syntax in literalSyntaxes)
224+
{
225+
if (syntax == null)
226+
{
227+
continue;
228+
}
229+
else if (!seenFirstLiteral)
230+
{
231+
// Set the parent and position of the merged literal to the value of the first non-null literal.
232+
parent = syntax.Parent;
233+
position = syntax.Position;
234+
seenFirstLiteral = true;
235+
}
236+
237+
foreach (var token in syntax.TextTokens)
238+
{
239+
builder.Add(token.Green);
240+
}
241+
}
242+
243+
var mergedLiteralSyntax = Syntax.InternalSyntax.SyntaxFactory.HtmlTextLiteral(
244+
builder.ToList<Syntax.InternalSyntax.SyntaxToken>());
245+
246+
return (HtmlTextLiteralSyntax)mergedLiteralSyntax.CreateRed(parent, position);
247+
}
248+
249+
private SourceSpan GetSourceSpanForNode(SyntaxNode node)
250+
{
251+
try
252+
{
253+
if (_source.Length == 0)
254+
{
255+
// Just a marker symbol
256+
return new SourceSpan(_source.FilePath, 0, 0, 0, node.FullWidth);
257+
}
258+
if (node.Position >= _source.Length)
259+
{
260+
// E.g. Marker symbol at the end of the document
261+
var lastLocation = _source.Lines.GetLocation(_source.Length - 1);
262+
return new SourceSpan(
263+
lastLocation.FilePath,
264+
lastLocation.AbsoluteIndex + 1,
265+
lastLocation.LineIndex,
266+
lastLocation.CharacterIndex + 1,
267+
node.FullWidth);
268+
}
269+
270+
return node.GetSourceSpan(_source);
271+
}
272+
catch (IndexOutOfRangeException)
273+
{
274+
Debug.Assert(false, "Node position should stay within document length.");
275+
return new SourceSpan(_source.FilePath, node.Position, 0, 0, node.FullWidth);
276+
}
277+
}
278+
}
279+
}

src/Microsoft.AspNetCore.Razor.Language/DefaultDirectiveSyntaxTreePass.cs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using Microsoft.AspNetCore.Razor.Language.Extensions;
77
using Microsoft.AspNetCore.Razor.Language.Legacy;
8+
using Microsoft.AspNetCore.Razor.Language.Syntax;
89

910
namespace Microsoft.AspNetCore.Razor.Language
1011
{
@@ -24,13 +25,52 @@ public RazorSyntaxTree Execute(RazorCodeDocument codeDocument, RazorSyntaxTree s
2425
throw new ArgumentNullException(nameof(syntaxTree));
2526
}
2627

27-
var sectionVerifier = new NestedSectionVerifier();
28-
sectionVerifier.Verify(syntaxTree);
28+
if (syntaxTree is LegacyRazorSyntaxTree)
29+
{
30+
var legacySectionVerifier = new LegacyNestedSectionVerifier();
31+
legacySectionVerifier.Verify(syntaxTree);
32+
return syntaxTree;
33+
}
34+
35+
var sectionVerifier = new NestedSectionVerifier(syntaxTree);
36+
return sectionVerifier.Verify();
37+
}
38+
39+
private class NestedSectionVerifier : SyntaxRewriter
40+
{
41+
private int _nestedLevel;
42+
private RazorSyntaxTree _syntaxTree;
43+
44+
public NestedSectionVerifier(RazorSyntaxTree syntaxTree)
45+
{
46+
_syntaxTree = syntaxTree;
47+
}
48+
49+
public RazorSyntaxTree Verify()
50+
{
51+
var root = Visit(_syntaxTree.NewRoot);
52+
var rewrittenTree = new DefaultRazorSyntaxTree(root, _syntaxTree.Source, _syntaxTree.Diagnostics, _syntaxTree.Options);
53+
return rewrittenTree;
54+
}
2955

30-
return syntaxTree;
56+
public override SyntaxNode VisitCSharpDirective(CSharpDirectiveSyntax node)
57+
{
58+
if (_nestedLevel > 0)
59+
{
60+
var directiveStart = node.Transition.GetSourceLocation(_syntaxTree.Source);
61+
var errorLength = /* @ */ 1 + SectionDirective.Directive.Directive.Length;
62+
var error = RazorDiagnosticFactory.CreateParsing_SectionsCannotBeNested(new SourceSpan(directiveStart, errorLength));
63+
node = node.AppendDiagnostic(error);
64+
}
65+
_nestedLevel++;
66+
var result = base.VisitCSharpDirective(node);
67+
_nestedLevel--;
68+
69+
return result;
70+
}
3171
}
3272

33-
private class NestedSectionVerifier : ParserVisitor
73+
private class LegacyNestedSectionVerifier : ParserVisitor
3474
{
3575
private int _nestedLevel;
3676

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using Microsoft.AspNetCore.Razor.Language.Legacy;
7+
using Microsoft.AspNetCore.Razor.Language.Syntax;
68

79
namespace Microsoft.AspNetCore.Razor.Language
810
{
911
internal class DefaultRazorSyntaxTree : RazorSyntaxTree
1012
{
1113
public DefaultRazorSyntaxTree(
12-
Block root,
14+
SyntaxNode root,
1315
RazorSourceDocument source,
1416
IReadOnlyList<RazorDiagnostic> diagnostics,
1517
RazorParserOptions options)
1618
{
17-
Root = root;
19+
NewRoot = root;
1820
Source = source;
1921
Diagnostics = diagnostics;
2022
Options = options;
@@ -24,8 +26,11 @@ public DefaultRazorSyntaxTree(
2426

2527
public override RazorParserOptions Options { get; }
2628

27-
internal override Block Root { get; }
29+
internal override SyntaxNode NewRoot { get; }
2830

2931
public override RazorSourceDocument Source { get; }
32+
33+
// Temporary
34+
internal override Block Root => throw new NotImplementedException();
3035
}
3136
}

0 commit comments

Comments
 (0)