diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanRewriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanRewriter.cs index dedb8c22fd..724203bd0a 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanRewriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanRewriter.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language { internal class ClassifiedSpanRewriter : SyntaxRewriter { - public override SyntaxNode VisitMarkupTagBlock(MarkupTagBlockSyntax node) + public override SyntaxNode VisitMarkupStartTag(MarkupStartTagSyntax node) { SpanContext latestSpanContext = null; var newChildren = SyntaxListBuilder.Create(); @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Razor.Language AddLiteralIfExists(); - return SyntaxFactory.MarkupTagBlock(newChildren.ToList()).Green.CreateRed(node.Parent, node.Position); + return SyntaxFactory.MarkupStartTag(newChildren.ToList()).Green.CreateRed(node.Parent, node.Position); void AddLiteralIfExists() { diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanVisitor.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanVisitor.cs index 416765c992..f6711c0ed5 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanVisitor.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ClassifiedSpanVisitor.cs @@ -106,9 +106,14 @@ namespace Microsoft.AspNetCore.Razor.Language base.VisitMarkupTagHelperAttributeValue(node); } - public override void VisitMarkupTagBlock(MarkupTagBlockSyntax node) + public override void VisitMarkupStartTag(MarkupStartTagSyntax node) { - WriteBlock(node, BlockKindInternal.Tag, base.VisitMarkupTagBlock); + WriteBlock(node, BlockKindInternal.Tag, base.VisitMarkupStartTag); + } + + public override void VisitMarkupEndTag(MarkupEndTagSyntax node) + { + WriteBlock(node, BlockKindInternal.Tag, base.VisitMarkupEndTag); } public override void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/RazorParser.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/RazorParser.cs index c840bcb707..0caff21144 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/RazorParser.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/RazorParser.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Razor.Language.Syntax; namespace Microsoft.AspNetCore.Razor.Language.Legacy { @@ -41,7 +42,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var diagnostics = context.ErrorSink.Errors; var root = markupParser.ParseDocument().CreateRed(); - return RazorSyntaxTree.Create(root, source, diagnostics, Options); + + var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, Options); + + // Group markup elements + syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree); + return syntaxTree; } } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperBlockRewriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperBlockRewriter.cs index 4c11269695..fefcf4aeee 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperBlockRewriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperBlockRewriter.cs @@ -10,13 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { internal static class TagHelperBlockRewriter { - private static readonly string StringTypeName = typeof(string).FullName; - public static MarkupTagHelperStartTagSyntax Rewrite( string tagName, bool validStructure, RazorParserFeatureFlags featureFlags, - MarkupTagBlockSyntax tag, + MarkupStartTagSyntax tag, TagHelperBinding bindingResult, ErrorSink errorSink, RazorSourceDocument source) @@ -28,7 +26,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy } public static TagMode GetTagMode( - MarkupTagBlockSyntax tagBlock, + MarkupStartTagSyntax tagBlock, TagHelperBinding bindingResult, ErrorSink errorSink) { @@ -57,7 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy private static SyntaxList GetRewrittenChildren( string tagName, bool validStructure, - MarkupTagBlockSyntax tagBlock, + MarkupStartTagSyntax tagBlock, TagHelperBinding bindingResult, RazorParserFeatureFlags featureFlags, ErrorSink errorSink, diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperParseTreeRewriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperParseTreeRewriter.cs index 18b1cf5c4d..ca0bc08809 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperParseTreeRewriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TagHelperParseTreeRewriter.cs @@ -15,7 +15,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy public static RazorSyntaxTree Rewrite(RazorSyntaxTree syntaxTree, string tagHelperPrefix, IEnumerable descriptors) { var errorSink = new ErrorSink(); - syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree); var rewriter = new Rewriter( syntaxTree.Source, @@ -33,8 +32,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList).OrderBy(error => error.Span.AbsoluteIndex); var newSyntaxTree = RazorSyntaxTree.Create(rewritten, syntaxTree.Source, diagnostics, syntaxTree.Options); - newSyntaxTree = MarkupElementRewriter.RemoveMarkupElements(newSyntaxTree); - return newSyntaxTree; } @@ -103,7 +100,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy TagHelperInfo tagHelperInfo = null; // Visit the start tag. - var startTag = (MarkupTagBlockSyntax)Visit(node.StartTag); + var startTag = (MarkupStartTagSyntax)Visit(node.StartTag); if (startTag != null) { var tagName = startTag.GetTagName(); @@ -145,7 +142,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy else { // Non-TagHelper tag. - ValidateParentAllowsPlainTag(startTag); + ValidateParentAllowsPlainStartTag(startTag); if (!startTag.IsSelfClosing() && !startTag.IsVoidElement()) { @@ -159,7 +156,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var body = VisitList(node.Body); // Visit end tag. - var endTag = (MarkupTagBlockSyntax)Visit(node.EndTag); + var endTag = (MarkupEndTagSyntax)Visit(node.EndTag); if (endTag != null) { var tagName = endTag.GetTagName(); @@ -181,9 +178,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { // Standalone end tag. We may need to error if it is not supposed to be here. // If there was a corresponding start tag, we would have already added this error. - ValidateParentAllowsPlainTag(endTag); + ValidateParentAllowsPlainEndTag(endTag); } - else if (!endTag.IsVoidElement()) + else { // Since a start tag exists, we must already be tracking it. // Pop the stack as we're done with the end tag. @@ -205,7 +202,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy public override SyntaxNode VisitMarkupTextLiteral(MarkupTextLiteralSyntax node) { - var tagParent = node.FirstAncestorOrSelf(n => n is MarkupTagBlockSyntax); + var tagParent = node.FirstAncestorOrSelf(n => n is MarkupStartTagSyntax || n is MarkupEndTagSyntax); var isPartofTagBlock = tagParent != null; if (!isPartofTagBlock) { @@ -215,13 +212,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return base.VisitMarkupTextLiteral(node); } - private bool TryRewriteTagHelperStart(MarkupTagBlockSyntax tagBlock, out MarkupTagHelperStartTagSyntax rewritten, out TagHelperInfo tagHelperInfo) + private bool TryRewriteTagHelperStart(MarkupStartTagSyntax startTag, out MarkupTagHelperStartTagSyntax rewritten, out TagHelperInfo tagHelperInfo) { rewritten = null; tagHelperInfo = null; // Get tag name of the current block - var tagName = tagBlock.GetTagName(); + var tagName = startTag.GetTagName(); // Could not determine tag name, it can't be a TagHelper, continue on and track the element. if (tagName == null) @@ -231,7 +228,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy TagHelperBinding tagHelperBinding; - if (!IsPotentialTagHelper(tagName, tagBlock)) + if (!IsPotentialTagHelperStart(tagName, startTag)) { return false; } @@ -240,7 +237,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var tagNameScope = tracker?.TagName ?? string.Empty; // We're now in a start tag block, we first need to see if the tag block is a tag helper. - var elementAttributes = GetAttributeNameValuePairs(tagBlock); + var elementAttributes = GetAttributeNameValuePairs(startTag); tagHelperBinding = _tagHelperBinder.GetBinding( tagName, @@ -264,29 +261,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return false; } - ValidateParentAllowsTagHelper(tagName, tagBlock); - ValidateBinding(tagHelperBinding, tagName, tagBlock); + ValidateParentAllowsTagHelper(tagName, startTag); + ValidateBinding(tagHelperBinding, tagName, startTag); // We're in a start TagHelper block. - var validTagStructure = ValidateTagSyntax(tagName, tagBlock); + var validTagStructure = ValidateStartTagSyntax(tagName, startTag); - var startTag = TagHelperBlockRewriter.Rewrite( + var rewrittenStartTag = TagHelperBlockRewriter.Rewrite( tagName, validTagStructure, _featureFlags, - tagBlock, + startTag, tagHelperBinding, _errorSink, _source); - var tagMode = TagHelperBlockRewriter.GetTagMode(tagBlock, tagHelperBinding, _errorSink); + var tagMode = TagHelperBlockRewriter.GetTagMode(startTag, tagHelperBinding, _errorSink); tagHelperInfo = new TagHelperInfo(tagName, tagMode, tagHelperBinding); - rewritten = startTag; + rewritten = rewrittenStartTag; return true; } - private bool TryRewriteTagHelperEnd(MarkupTagBlockSyntax tagBlock, out MarkupTagHelperEndTagSyntax rewritten) + private bool TryRewriteTagHelperEnd(MarkupEndTagSyntax tagBlock, out MarkupTagHelperEndTagSyntax rewritten) { rewritten = null; var tagName = tagBlock.GetTagName(); @@ -298,7 +295,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var tracker = CurrentTagHelperTracker; var tagNameScope = tracker?.TagName ?? string.Empty; - if (!IsPotentialTagHelper(tagName, tagBlock)) + if (!IsPotentialTagHelperEnd(tagName, tagBlock)) { return false; } @@ -315,7 +312,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return false; } - ValidateTagSyntax(tagName, tagBlock); + ValidateEndTagSyntax(tagName, tagBlock); _trackerStack.Pop(); } @@ -360,10 +357,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy } // Internal for testing - internal IReadOnlyList> GetAttributeNameValuePairs(MarkupTagBlockSyntax tagBlock) + internal IReadOnlyList> GetAttributeNameValuePairs(MarkupStartTagSyntax tagBlock) { // Need to calculate how many children we should take that represent the attributes. - var childrenOffset = IsPartialTag(tagBlock) ? 0 : 1; + var childrenOffset = IsPartialStartTag(tagBlock) ? 0 : 1; var childCount = tagBlock.Children.Count - childrenOffset; if (childCount <= 1) @@ -436,19 +433,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return attributes; } - private void ValidateParentAllowsTagHelper(string tagName, MarkupTagBlockSyntax tagBlock) + private void ValidateParentAllowsTagHelper(string tagName, MarkupStartTagSyntax tagBlock) { if (HasAllowedChildren() && !CurrentTagHelperTracker.PrefixedAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) { - OnAllowedChildrenTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); + OnAllowedChildrenStartTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); } } private void ValidateBinding( TagHelperBinding bindingResult, string tagName, - MarkupTagBlockSyntax tagBlock) + MarkupStartTagSyntax tagBlock) { // Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have // multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified). @@ -479,12 +476,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy } } - private bool ValidateTagSyntax(string tagName, MarkupTagBlockSyntax tag) + private bool ValidateStartTagSyntax(string tagName, MarkupStartTagSyntax tag) { // We assume an invalid syntax until we verify that the tag meets all of our "valid syntax" criteria. - if (IsPartialTag(tag)) + if (IsPartialStartTag(tag)) { - var errorStart = GetTagDeclarationErrorStart(tag); + var errorStart = GetStartTagDeclarationErrorStart(tag, _source); _errorSink.OnError( RazorDiagnosticFactory.CreateParsing_TagHelperMissingCloseAngle( @@ -496,7 +493,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return true; } - private bool IsPotentialTagHelper(string tagName, MarkupTagBlockSyntax childBlock) + private bool ValidateEndTagSyntax(string tagName, MarkupEndTagSyntax tag) + { + // We assume an invalid syntax until we verify that the tag meets all of our "valid syntax" criteria. + if (IsPartialEndTag(tag)) + { + var errorStart = GetEndTagDeclarationErrorStart(tag, _source); + + _errorSink.OnError( + RazorDiagnosticFactory.CreateParsing_TagHelperMissingCloseAngle( + new SourceSpan(errorStart, tagName.Length), tagName)); + + return false; + } + + return true; + } + + private bool IsPotentialTagHelperStart(string tagName, MarkupStartTagSyntax childBlock) { Debug.Assert(childBlock.Children.Count > 0); var child = childBlock.Children[0]; @@ -505,14 +519,37 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy child.Kind != SyntaxKind.MarkupTransition; } - private SourceLocation GetTagDeclarationErrorStart(MarkupTagBlockSyntax tagBlock) + private bool IsPotentialTagHelperEnd(string tagName, MarkupEndTagSyntax childBlock) { - var advanceBy = IsEndTag(tagBlock) ? " 0); + var child = childBlock.Children[0]; - return SourceLocationTracker.Advance(tagBlock.GetSourceLocation(_source), advanceBy); + return !string.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) || + child.Kind != SyntaxKind.MarkupTransition; } - private static bool IsPartialTag(MarkupTagBlockSyntax tagBlock) + private static bool IsPartialStartTag(MarkupStartTagSyntax tagBlock) + { + // No need to validate the tag end because in order to be a tag block it must start with '<'. + var tagEnd = tagBlock.Children[tagBlock.Children.Count - 1]; + + // If our tag end is not a markup span it means it's some sort of code SyntaxTreeNode (not a valid format) + if (tagEnd != null && tagEnd is MarkupTextLiteralSyntax tagEndLiteral) + { + var endToken = tagEndLiteral.LiteralTokens.Count > 0 ? + tagEndLiteral.LiteralTokens[tagEndLiteral.LiteralTokens.Count - 1] : + null; + + if (endToken != null && endToken.Kind == SyntaxKind.CloseAngle) + { + return false; + } + } + + return true; + } + + private static bool IsPartialEndTag(MarkupEndTagSyntax tagBlock) { // No need to validate the tag end because in order to be a tag block it must start with '<'. var tagEnd = tagBlock.Children[tagBlock.Children.Count - 1]; @@ -566,7 +603,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy } } - private void ValidateParentAllowsPlainTag(MarkupTagBlockSyntax tagBlock) + private void ValidateParentAllowsPlainStartTag(MarkupStartTagSyntax tagBlock) { var tagName = tagBlock.GetTagName(); @@ -595,7 +632,40 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var allowedChildren = tagHelperBinding != null ? CurrentTagHelperTracker.PrefixedAllowedChildren : CurrentTagHelperTracker.AllowedChildren; if (!allowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) { - OnAllowedChildrenTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); + OnAllowedChildrenStartTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); + } + } + + private void ValidateParentAllowsPlainEndTag(MarkupEndTagSyntax tagBlock) + { + var tagName = tagBlock.GetTagName(); + + // Treat partial tags such as '>(), + parentTagName: CurrentParentTagName, + parentIsTagHelper: CurrentParentIsTagHelper); + + // If we found a binding for the current tag, then it is a tag helper. Use the prefixed allowed children to compare. + var allowedChildren = tagHelperBinding != null ? CurrentTagHelperTracker.PrefixedAllowedChildren : CurrentTagHelperTracker.AllowedChildren; + if (!allowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) + { + OnAllowedChildrenEndTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); } } @@ -632,15 +702,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return commentParent != null; } - private static void OnAllowedChildrenTagError( + private static void OnAllowedChildrenStartTagError( TagHelperTracker tracker, string tagName, - MarkupTagBlockSyntax tagBlock, + MarkupStartTagSyntax tagBlock, ErrorSink errorSink, RazorSourceDocument source) { var allowedChildrenString = string.Join(", ", tracker.AllowedChildren); - var errorStart = GetTagDeclarationErrorStart(tagBlock, source); + var errorStart = GetStartTagDeclarationErrorStart(tagBlock, source); errorSink.OnError( RazorDiagnosticFactory.CreateTagHelper_InvalidNestedTag( @@ -650,21 +720,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy allowedChildrenString)); } - private static SourceLocation GetTagDeclarationErrorStart(MarkupTagBlockSyntax tagBlock, RazorSourceDocument source) + private static void OnAllowedChildrenEndTagError( + TagHelperTracker tracker, + string tagName, + MarkupEndTagSyntax tagBlock, + ErrorSink errorSink, + RazorSourceDocument source) { - var advanceBy = IsEndTag(tagBlock) ? " Body { get { return new SyntaxList(_body); } } - public MarkupTagBlockSyntax EndTag { get { return _endTag; } } + public MarkupEndTagSyntax EndTag { get { return _endTag; } } internal override GreenNode GetSlot(int index) { @@ -1518,7 +1518,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax visitor.VisitMarkupElement(this); } - public MarkupElementSyntax Update(MarkupTagBlockSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList body, MarkupTagBlockSyntax endTag) + public MarkupElementSyntax Update(MarkupStartTagSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList body, MarkupEndTagSyntax endTag) { if (startTag != StartTag || body != Body || endTag != EndTag) { @@ -1546,6 +1546,168 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax } } + internal sealed partial class MarkupStartTagSyntax : RazorBlockSyntax + { + private readonly GreenNode _children; + + internal MarkupStartTagSyntax(SyntaxKind kind, GreenNode children, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations) + : base(kind, diagnostics, annotations) + { + SlotCount = 1; + if (children != null) + { + AdjustFlagsAndWidth(children); + _children = children; + } + } + + + internal MarkupStartTagSyntax(SyntaxKind kind, GreenNode children) + : base(kind) + { + SlotCount = 1; + if (children != null) + { + AdjustFlagsAndWidth(children); + _children = children; + } + } + + public override SyntaxList Children { get { return new SyntaxList(_children); } } + + internal override GreenNode GetSlot(int index) + { + switch (index) + { + case 0: return _children; + default: return null; + } + } + + internal override SyntaxNode CreateRed(SyntaxNode parent, int position) + { + return new Syntax.MarkupStartTagSyntax(this, parent, position); + } + + public override TResult Accept(SyntaxVisitor visitor) + { + return visitor.VisitMarkupStartTag(this); + } + + public override void Accept(SyntaxVisitor visitor) + { + visitor.VisitMarkupStartTag(this); + } + + public MarkupStartTagSyntax Update(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList children) + { + if (children != Children) + { + var newNode = SyntaxFactory.MarkupStartTag(children); + var diags = GetDiagnostics(); + if (diags != null && diags.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = GetAnnotations(); + if (annotations != null && annotations.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; + } + + return this; + } + + internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics) + { + return new MarkupStartTagSyntax(Kind, _children, diagnostics, GetAnnotations()); + } + + internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) + { + return new MarkupStartTagSyntax(Kind, _children, GetDiagnostics(), annotations); + } + } + + internal sealed partial class MarkupEndTagSyntax : RazorBlockSyntax + { + private readonly GreenNode _children; + + internal MarkupEndTagSyntax(SyntaxKind kind, GreenNode children, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations) + : base(kind, diagnostics, annotations) + { + SlotCount = 1; + if (children != null) + { + AdjustFlagsAndWidth(children); + _children = children; + } + } + + + internal MarkupEndTagSyntax(SyntaxKind kind, GreenNode children) + : base(kind) + { + SlotCount = 1; + if (children != null) + { + AdjustFlagsAndWidth(children); + _children = children; + } + } + + public override SyntaxList Children { get { return new SyntaxList(_children); } } + + internal override GreenNode GetSlot(int index) + { + switch (index) + { + case 0: return _children; + default: return null; + } + } + + internal override SyntaxNode CreateRed(SyntaxNode parent, int position) + { + return new Syntax.MarkupEndTagSyntax(this, parent, position); + } + + public override TResult Accept(SyntaxVisitor visitor) + { + return visitor.VisitMarkupEndTag(this); + } + + public override void Accept(SyntaxVisitor visitor) + { + visitor.VisitMarkupEndTag(this); + } + + public MarkupEndTagSyntax Update(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList children) + { + if (children != Children) + { + var newNode = SyntaxFactory.MarkupEndTag(children); + var diags = GetDiagnostics(); + if (diags != null && diags.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = GetAnnotations(); + if (annotations != null && annotations.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; + } + + return this; + } + + internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics) + { + return new MarkupEndTagSyntax(Kind, _children, diagnostics, GetAnnotations()); + } + + internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) + { + return new MarkupEndTagSyntax(Kind, _children, GetDiagnostics(), annotations); + } + } + internal sealed partial class MarkupTagHelperElementSyntax : MarkupSyntaxNode { private readonly MarkupTagHelperStartTagSyntax _startTag; @@ -3383,6 +3545,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax return DefaultVisit(node); } + public virtual TResult VisitMarkupStartTag(MarkupStartTagSyntax node) + { + return DefaultVisit(node); + } + + public virtual TResult VisitMarkupEndTag(MarkupEndTagSyntax node) + { + return DefaultVisit(node); + } + public virtual TResult VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) { return DefaultVisit(node); @@ -3572,6 +3744,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax DefaultVisit(node); } + public virtual void VisitMarkupStartTag(MarkupStartTagSyntax node) + { + DefaultVisit(node); + } + + public virtual void VisitMarkupEndTag(MarkupEndTagSyntax node) + { + DefaultVisit(node); + } + public virtual void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) { DefaultVisit(node); @@ -3786,12 +3968,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax public override GreenNode VisitMarkupElement(MarkupElementSyntax node) { - var startTag = (MarkupTagBlockSyntax)Visit(node.StartTag); + var startTag = (MarkupStartTagSyntax)Visit(node.StartTag); var body = VisitList(node.Body); - var endTag = (MarkupTagBlockSyntax)Visit(node.EndTag); + var endTag = (MarkupEndTagSyntax)Visit(node.EndTag); return node.Update(startTag, body, endTag); } + public override GreenNode VisitMarkupStartTag(MarkupStartTagSyntax node) + { + var children = VisitList(node.Children); + return node.Update(children); + } + + public override GreenNode VisitMarkupEndTag(MarkupEndTagSyntax node) + { + var children = VisitList(node.Children); + return node.Update(children); + } + public override GreenNode VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) { var startTag = (MarkupTagHelperStartTagSyntax)Visit(node.StartTag); @@ -4108,13 +4302,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax return result; } - public static MarkupElementSyntax MarkupElement(MarkupTagBlockSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList body, MarkupTagBlockSyntax endTag) + public static MarkupElementSyntax MarkupElement(MarkupStartTagSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList body, MarkupEndTagSyntax endTag) { var result = new MarkupElementSyntax(SyntaxKind.MarkupElement, startTag, body.Node, endTag); return result; } + public static MarkupStartTagSyntax MarkupStartTag(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList children) + { + var result = new MarkupStartTagSyntax(SyntaxKind.MarkupStartTag, children.Node); + + return result; + } + + public static MarkupEndTagSyntax MarkupEndTag(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList children) + { + var result = new MarkupEndTagSyntax(SyntaxKind.MarkupEndTag, children.Node); + + return result; + } + public static MarkupTagHelperElementSyntax MarkupTagHelperElement(MarkupTagHelperStartTagSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList body, MarkupTagHelperEndTagSyntax endTag) { if (startTag == null) @@ -4343,6 +4551,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax typeof(MarkupLiteralAttributeValueSyntax), typeof(MarkupDynamicAttributeValueSyntax), typeof(MarkupElementSyntax), + typeof(MarkupStartTagSyntax), + typeof(MarkupEndTagSyntax), typeof(MarkupTagHelperElementSyntax), typeof(MarkupTagHelperStartTagSyntax), typeof(MarkupTagHelperEndTagSyntax), diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Main.Generated.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Main.Generated.cs index 019d64dc8d..a5f908438a 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Main.Generated.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Main.Generated.cs @@ -113,6 +113,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax return DefaultVisit(node); } + /// Called when the visitor visits a MarkupStartTagSyntax node. + public virtual TResult VisitMarkupStartTag(MarkupStartTagSyntax node) + { + return DefaultVisit(node); + } + + /// Called when the visitor visits a MarkupEndTagSyntax node. + public virtual TResult VisitMarkupEndTag(MarkupEndTagSyntax node) + { + return DefaultVisit(node); + } + /// Called when the visitor visits a MarkupTagHelperElementSyntax node. public virtual TResult VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) { @@ -338,6 +350,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax DefaultVisit(node); } + /// Called when the visitor visits a MarkupStartTagSyntax node. + public virtual void VisitMarkupStartTag(MarkupStartTagSyntax node) + { + DefaultVisit(node); + } + + /// Called when the visitor visits a MarkupEndTagSyntax node. + public virtual void VisitMarkupEndTag(MarkupEndTagSyntax node) + { + DefaultVisit(node); + } + /// Called when the visitor visits a MarkupTagHelperElementSyntax node. public virtual void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) { @@ -572,12 +596,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax public override SyntaxNode VisitMarkupElement(MarkupElementSyntax node) { - var startTag = (MarkupTagBlockSyntax)Visit(node.StartTag); + var startTag = (MarkupStartTagSyntax)Visit(node.StartTag); var body = VisitList(node.Body); - var endTag = (MarkupTagBlockSyntax)Visit(node.EndTag); + var endTag = (MarkupEndTagSyntax)Visit(node.EndTag); return node.Update(startTag, body, endTag); } + public override SyntaxNode VisitMarkupStartTag(MarkupStartTagSyntax node) + { + var children = VisitList(node.Children); + return node.Update(children); + } + + public override SyntaxNode VisitMarkupEndTag(MarkupEndTagSyntax node) + { + var children = VisitList(node.Children); + return node.Update(children); + } + public override SyntaxNode VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) { var startTag = (MarkupTagHelperStartTagSyntax)Visit(node.StartTag); @@ -962,15 +998,39 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax } /// Creates a new MarkupElementSyntax instance. - public static MarkupElementSyntax MarkupElement(MarkupTagBlockSyntax startTag, SyntaxList body, MarkupTagBlockSyntax endTag) + public static MarkupElementSyntax MarkupElement(MarkupStartTagSyntax startTag, SyntaxList body, MarkupEndTagSyntax endTag) { - return (MarkupElementSyntax)InternalSyntax.SyntaxFactory.MarkupElement(startTag == null ? null : (InternalSyntax.MarkupTagBlockSyntax)startTag.Green, body.Node.ToGreenList(), endTag == null ? null : (InternalSyntax.MarkupTagBlockSyntax)endTag.Green).CreateRed(); + return (MarkupElementSyntax)InternalSyntax.SyntaxFactory.MarkupElement(startTag == null ? null : (InternalSyntax.MarkupStartTagSyntax)startTag.Green, body.Node.ToGreenList(), endTag == null ? null : (InternalSyntax.MarkupEndTagSyntax)endTag.Green).CreateRed(); } /// Creates a new MarkupElementSyntax instance. public static MarkupElementSyntax MarkupElement(SyntaxList body = default(SyntaxList)) { - return SyntaxFactory.MarkupElement(default(MarkupTagBlockSyntax), body, default(MarkupTagBlockSyntax)); + return SyntaxFactory.MarkupElement(default(MarkupStartTagSyntax), body, default(MarkupEndTagSyntax)); + } + + /// Creates a new MarkupStartTagSyntax instance. + public static MarkupStartTagSyntax MarkupStartTag(SyntaxList children) + { + return (MarkupStartTagSyntax)InternalSyntax.SyntaxFactory.MarkupStartTag(children.Node.ToGreenList()).CreateRed(); + } + + /// Creates a new MarkupStartTagSyntax instance. + public static MarkupStartTagSyntax MarkupStartTag() + { + return SyntaxFactory.MarkupStartTag(default(SyntaxList)); + } + + /// Creates a new MarkupEndTagSyntax instance. + public static MarkupEndTagSyntax MarkupEndTag(SyntaxList children) + { + return (MarkupEndTagSyntax)InternalSyntax.SyntaxFactory.MarkupEndTag(children.Node.ToGreenList()).CreateRed(); + } + + /// Creates a new MarkupEndTagSyntax instance. + public static MarkupEndTagSyntax MarkupEndTag() + { + return SyntaxFactory.MarkupEndTag(default(SyntaxList)); } /// Creates a new MarkupTagHelperElementSyntax instance. diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Syntax.Generated.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Syntax.Generated.cs index 4c0878d99f..92ba8867d0 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Syntax.Generated.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/Generated/Syntax.xml.Syntax.Generated.cs @@ -1382,16 +1382,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax internal sealed partial class MarkupElementSyntax : MarkupSyntaxNode { - private MarkupTagBlockSyntax _startTag; + private MarkupStartTagSyntax _startTag; private SyntaxNode _body; - private MarkupTagBlockSyntax _endTag; + private MarkupEndTagSyntax _endTag; internal MarkupElementSyntax(GreenNode green, SyntaxNode parent, int position) : base(green, parent, position) { } - public MarkupTagBlockSyntax StartTag + public MarkupStartTagSyntax StartTag { get { @@ -1407,7 +1407,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax } } - public MarkupTagBlockSyntax EndTag + public MarkupEndTagSyntax EndTag { get { @@ -1446,7 +1446,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax visitor.VisitMarkupElement(this); } - public MarkupElementSyntax Update(MarkupTagBlockSyntax startTag, SyntaxList body, MarkupTagBlockSyntax endTag) + public MarkupElementSyntax Update(MarkupStartTagSyntax startTag, SyntaxList body, MarkupEndTagSyntax endTag) { if (startTag != StartTag || body != Body || endTag != EndTag) { @@ -1460,7 +1460,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax return this; } - public MarkupElementSyntax WithStartTag(MarkupTagBlockSyntax startTag) + public MarkupElementSyntax WithStartTag(MarkupStartTagSyntax startTag) { return Update(startTag, Body, EndTag); } @@ -1470,14 +1470,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax return Update(StartTag, body, EndTag); } - public MarkupElementSyntax WithEndTag(MarkupTagBlockSyntax endTag) + public MarkupElementSyntax WithEndTag(MarkupEndTagSyntax endTag) { return Update(StartTag, Body, endTag); } public MarkupElementSyntax AddStartTagChildren(params RazorSyntaxNode[] items) { - var _startTag = this.StartTag ?? SyntaxFactory.MarkupTagBlock(); + var _startTag = this.StartTag ?? SyntaxFactory.MarkupStartTag(); return this.WithStartTag(_startTag.WithChildren(_startTag.Children.AddRange(items))); } @@ -1488,11 +1488,153 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax public MarkupElementSyntax AddEndTagChildren(params RazorSyntaxNode[] items) { - var _endTag = this.EndTag ?? SyntaxFactory.MarkupTagBlock(); + var _endTag = this.EndTag ?? SyntaxFactory.MarkupEndTag(); return this.WithEndTag(_endTag.WithChildren(_endTag.Children.AddRange(items))); } } + internal sealed partial class MarkupStartTagSyntax : RazorBlockSyntax + { + private SyntaxNode _children; + + internal MarkupStartTagSyntax(GreenNode green, SyntaxNode parent, int position) + : base(green, parent, position) + { + } + + public override SyntaxList Children + { + get + { + return new SyntaxList(GetRed(ref _children, 0)); + } + } + + internal override SyntaxNode GetNodeSlot(int index) + { + switch (index) + { + case 0: return GetRedAtZero(ref _children); + default: return null; + } + } + internal override SyntaxNode GetCachedSlot(int index) + { + switch (index) + { + case 0: return _children; + default: return null; + } + } + + public override TResult Accept(SyntaxVisitor visitor) + { + return visitor.VisitMarkupStartTag(this); + } + + public override void Accept(SyntaxVisitor visitor) + { + visitor.VisitMarkupStartTag(this); + } + + public MarkupStartTagSyntax Update(SyntaxList children) + { + if (children != Children) + { + var newNode = SyntaxFactory.MarkupStartTag(children); + var annotations = GetAnnotations(); + if (annotations != null && annotations.Length > 0) + return newNode.WithAnnotations(annotations); + return newNode; + } + + return this; + } + + internal override RazorBlockSyntax WithChildrenCore(SyntaxList children) => WithChildren(children); + public new MarkupStartTagSyntax WithChildren(SyntaxList children) + { + return Update(children); + } + internal override RazorBlockSyntax AddChildrenCore(params RazorSyntaxNode[] items) => AddChildren(items); + + public new MarkupStartTagSyntax AddChildren(params RazorSyntaxNode[] items) + { + return WithChildren(this.Children.AddRange(items)); + } + } + + internal sealed partial class MarkupEndTagSyntax : RazorBlockSyntax + { + private SyntaxNode _children; + + internal MarkupEndTagSyntax(GreenNode green, SyntaxNode parent, int position) + : base(green, parent, position) + { + } + + public override SyntaxList Children + { + get + { + return new SyntaxList(GetRed(ref _children, 0)); + } + } + + internal override SyntaxNode GetNodeSlot(int index) + { + switch (index) + { + case 0: return GetRedAtZero(ref _children); + default: return null; + } + } + internal override SyntaxNode GetCachedSlot(int index) + { + switch (index) + { + case 0: return _children; + default: return null; + } + } + + public override TResult Accept(SyntaxVisitor visitor) + { + return visitor.VisitMarkupEndTag(this); + } + + public override void Accept(SyntaxVisitor visitor) + { + visitor.VisitMarkupEndTag(this); + } + + public MarkupEndTagSyntax Update(SyntaxList children) + { + if (children != Children) + { + var newNode = SyntaxFactory.MarkupEndTag(children); + var annotations = GetAnnotations(); + if (annotations != null && annotations.Length > 0) + return newNode.WithAnnotations(annotations); + return newNode; + } + + return this; + } + + internal override RazorBlockSyntax WithChildrenCore(SyntaxList children) => WithChildren(children); + public new MarkupEndTagSyntax WithChildren(SyntaxList children) + { + return Update(children); + } + internal override RazorBlockSyntax AddChildrenCore(params RazorSyntaxNode[] items) => AddChildren(items); + + public new MarkupEndTagSyntax AddChildren(params RazorSyntaxNode[] items) + { + return WithChildren(this.Children.AddRange(items)); + } + } + internal sealed partial class MarkupTagHelperElementSyntax : MarkupSyntaxNode { private MarkupTagHelperStartTagSyntax _startTag; diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/MarkupElementRewriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/MarkupElementRewriter.cs index 7aeda1c349..b0d9e82624 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/MarkupElementRewriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/MarkupElementRewriter.cs @@ -19,15 +19,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax return newSyntaxTree; } - public static RazorSyntaxTree RemoveMarkupElements(RazorSyntaxTree syntaxTree) - { - var rewriter = new RemoveMarkupElementRewriter(); - var rewrittenRoot = rewriter.Visit(syntaxTree.Root); - - var newSyntaxTree = RazorSyntaxTree.Create(rewrittenRoot, syntaxTree.Source, syntaxTree.Diagnostics, syntaxTree.Options); - return newSyntaxTree; - } - private class AddMarkupElementRewriter : SyntaxRewriter { private readonly Stack _startTagTracker = new Stack(); @@ -38,10 +29,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax public override SyntaxNode Visit(SyntaxNode node) { - node = base.Visit(node); - if (node != null) { + var diagnostics = node.GetDiagnostics(); + node = base.Visit(node); + if (diagnostics.Length > 0) + { + // Persist node diagnostics. + node = node.WithDiagnostics(diagnostics); + } + node = RewriteNode(node); } @@ -71,7 +68,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax var tagName = tagBlock.GetTagName(); if (string.IsNullOrWhiteSpace(tagName) || tagBlock.IsSelfClosing()) { - // Don't want to track incomplete, invalid (Eg. , < >), void or self-closing tags. + // Don't want to track incomplete, invalid (Eg. , < >) or self-closing tags. // Simply wrap it in a block with no body or start/end tag. if (IsEndTag(tagBlock)) { @@ -102,7 +99,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax if (!TryRecoverStartTag(rewrittenChildren, tagName, tagBlock)) { // Could not recover. The end tag doesn't have a corresponding start tag. Wrap it in a block and move on. - var rewritten = SyntaxFactory.MarkupElement(startTag: null, body: new SyntaxList(), endTag: tagBlock); + var rewritten = SyntaxFactory.MarkupElement(startTag: null, body: new SyntaxList(), endTag: GetEndTagSyntax(tagBlock)); TrackChild(rewritten, rewrittenChildren); } } @@ -149,7 +146,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax // The call to SyntaxNode.ReplaceNodes() later will take care removing the nodes whose replacement is null. var body = tagChildren.Where(t => t != null).ToList(); - var rewritten = SyntaxFactory.MarkupElement(startTag, new SyntaxList(body), endTag); + var rewritten = SyntaxFactory.MarkupElement( + GetStartTagSyntax(startTag), + new SyntaxList(body), + GetEndTagSyntax(endTag)); + if (startTag != null) { // If there was a start tag, that is where we want to put our new element. @@ -232,6 +233,26 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax return childContent.StartsWith(" - + - + + + + + + + + + diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxKind.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxKind.cs index d8c68ec77e..66be02fdfa 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxKind.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxKind.cs @@ -19,6 +19,8 @@ namespace Microsoft.AspNetCore.Razor.Language MarkupBlock, MarkupTransition, MarkupElement, + MarkupStartTag, + MarkupEndTag, MarkupTagBlock, MarkupTextLiteral, MarkupEphemeralTextLiteral, diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxNodeExtensions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxNodeExtensions.cs index 4955f15294..521bb4ed79 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxNodeExtensions.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxNodeExtensions.cs @@ -223,55 +223,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax public static string GetTagName(this MarkupTagBlockSyntax tagBlock) { - if (tagBlock == null) - { - throw new ArgumentNullException(nameof(tagBlock)); - } - - var child = tagBlock.Children[0]; - - if (tagBlock.Children.Count == 0 || !(child is MarkupTextLiteralSyntax)) - { - return null; - } - - var childLiteral = (MarkupTextLiteralSyntax)child; - SyntaxToken textToken = null; - for (var i = 0; i < childLiteral.LiteralTokens.Count; i++) - { - var token = childLiteral.LiteralTokens[i]; - - if (token != null && - (token.Kind == SyntaxKind.Whitespace || token.Kind == SyntaxKind.Text)) - { - textToken = token; - break; - } - } - - if (textToken == null) - { - return null; - } - - return textToken.Kind == SyntaxKind.Whitespace ? null : textToken.Content; + return GetTagNameCore(tagBlock); } - public static string GetTagName(this MarkupTagHelperStartTagSyntax tagBlock) + public static string GetTagName(this MarkupStartTagSyntax startTag) + { + return GetTagNameCore(startTag); + } + + public static string GetTagName(this MarkupEndTagSyntax endTag) + { + return GetTagNameCore(endTag); + } + + private static string GetTagNameCore(RazorBlockSyntax tagBlock) { if (tagBlock == null) { throw new ArgumentNullException(nameof(tagBlock)); } - var child = tagBlock.Children[0]; - - if (tagBlock.Children.Count == 0 || !(child is MarkupTextLiteralSyntax)) + if (tagBlock.Children.Count == 0 || !(tagBlock.Children[0] is MarkupTextLiteralSyntax childLiteral)) { return null; } - var childLiteral = (MarkupTextLiteralSyntax)child; SyntaxToken textToken = null; for (var i = 0; i < childLiteral.LiteralTokens.Count; i++) { @@ -294,6 +270,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax } public static bool IsSelfClosing(this MarkupTagBlockSyntax tagBlock) + { + return IsSelfClosingCore(tagBlock); + } + + public static bool IsSelfClosing(this MarkupStartTagSyntax startTag) + { + return IsSelfClosingCore(startTag); + } + + private static bool IsSelfClosingCore(RazorBlockSyntax tagBlock) { if (tagBlock == null) { @@ -305,14 +291,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax return lastChild?.GetContent().EndsWith("/>", StringComparison.Ordinal) ?? false; } - public static bool IsVoidElement(this MarkupTagBlockSyntax tagBlock) + public static bool IsVoidElement(this MarkupStartTagSyntax startTag) { - if (tagBlock == null) + if (startTag == null) { - throw new ArgumentNullException(nameof(tagBlock)); + throw new ArgumentNullException(nameof(startTag)); } - return ParserHelpers.VoidElements.Contains(tagBlock.GetTagName()); + return ParserHelpers.VoidElements.Contains(startTag.GetTagName()); } } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxSerializer.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxSerializer.cs index 5b167b510f..a811d79aa2 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxSerializer.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/SyntaxSerializer.cs @@ -287,7 +287,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax { return node.Kind == SyntaxKind.MarkupTextLiteral || node.Kind == SyntaxKind.MarkupEphemeralTextLiteral || - node.Kind == SyntaxKind.MarkupTagBlock || + node.Kind == SyntaxKind.MarkupStartTag || + node.Kind == SyntaxKind.MarkupEndTag || node.Kind == SyntaxKind.MarkupAttributeBlock || node.Kind == SyntaxKind.MarkupMinimizedAttributeBlock || node.Kind == SyntaxKind.MarkupTagHelperAttribute || diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs index 1e88100c46..22d088d7d5 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/ParserTestBase.cs @@ -13,6 +13,7 @@ using System.Text; using Xunit; using Xunit.Sdk; using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Razor.Language.Syntax; namespace Microsoft.AspNetCore.Razor.Language.Legacy { @@ -240,6 +241,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options); codeDocument.SetSyntaxTree(syntaxTree); + // Group markup elements + syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree); + var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass(); syntaxTree = defaultDirectivePass.Execute(codeDocument, syntaxTree); @@ -267,6 +271,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options); + // Group markup elements + syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree); + return syntaxTree; } @@ -300,6 +307,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options); + // Group markup elements + syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree); + return syntaxTree; } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/SyntaxNodeWriter.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/SyntaxNodeWriter.cs index 3d1faba30e..0974b6a7b9 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/SyntaxNodeWriter.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/SyntaxNodeWriter.cs @@ -203,7 +203,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax { return node.Kind == SyntaxKind.MarkupTextLiteral || node.Kind == SyntaxKind.MarkupEphemeralTextLiteral || - node.Kind == SyntaxKind.MarkupTagBlock || + node.Kind == SyntaxKind.MarkupStartTag || + node.Kind == SyntaxKind.MarkupEndTag || node.Kind == SyntaxKind.MarkupAttributeBlock || node.Kind == SyntaxKind.MarkupMinimizedAttributeBlock || node.Kind == SyntaxKind.MarkupTagHelperAttribute ||