diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/DirectiveTokenEditHandlerTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/DirectiveTokenEditHandlerTest.cs index c06b2d354b3..9286710aeb1 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/DirectiveTokenEditHandlerTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/DirectiveTokenEditHandlerTest.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.AspNetCore.Razor.Language.Legacy; using Microsoft.AspNetCore.Razor.Language.Syntax; +using Microsoft.AspNetCore.Razor.PooledObjects; using Xunit; namespace Microsoft.AspNetCore.Razor.Language.Test; @@ -63,13 +64,14 @@ public void CanAcceptChange_RejectsWhitespaceChanges(int index, int length, stri private static CSharpStatementLiteralSyntax GetSyntaxNode(DirectiveTokenEditHandler editHandler, string content) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; var tokens = NativeCSharpLanguageCharacteristics.Instance.TokenizeString(content).ToArray(); foreach (var token in tokens) { builder.Add((SyntaxToken)token.CreateRed()); } + var node = SyntaxFactory.CSharpStatementLiteral(builder.ToList(), SpanChunkGenerator.Null); return node.WithEditHandler(editHandler); diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Legacy/CodeBlockEditHandlerTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Legacy/CodeBlockEditHandlerTest.cs index 4e9f7392d21..7bb8db71ab6 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Legacy/CodeBlockEditHandlerTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Legacy/CodeBlockEditHandlerTest.cs @@ -6,6 +6,7 @@ using System.Linq; using Microsoft.AspNetCore.Razor.Language.Legacy; using Microsoft.AspNetCore.Razor.Language.Syntax; +using Microsoft.AspNetCore.Razor.PooledObjects; using Xunit; namespace Microsoft.AspNetCore.Razor.Language.Test.Legacy; @@ -281,7 +282,7 @@ public void ContainsInvalidContent_ValidContent_ReturnsFalse(string content) private static SyntaxNode GetSpan(SourceLocation start, string content) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; var tokens = NativeCSharpLanguageCharacteristics.Instance.TokenizeString(content).ToArray(); foreach (var token in tokens) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs index 526e82c95ef..388f1ea38c7 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs @@ -128,9 +128,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, Cancellation documentNode.Diagnostics.Add(diagnostic); } - if (imports is { IsDefault: false } importsArray) + if (!imports.IsDefaultOrEmpty) { - foreach (var import in importsArray) + foreach (var import in imports) { foreach (var diagnostic in import.Diagnostics) { @@ -492,38 +492,30 @@ public override void VisitCSharpStatementLiteral(CSharpStatementLiteralSyntax no return node.GetSourceSpan(SourceDocument); } - protected static SyntaxList MergeLiterals( - SyntaxList? literal1, - SyntaxList? literal2, - SyntaxList? literal3 = null, - SyntaxList? literal4 = null, - SyntaxList? literal5 = null) + protected static SyntaxList MergeTokenLists(params ReadOnlySpan?> tokenLists) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + var count = 0; - if (literal1 is { } tokens1) + foreach (var tokenList in tokenLists) { - builder.AddRange(tokens1); + count += tokenList?.Count ?? 0; } - if (literal2 is { } tokens2) + if (count == 0) { - builder.AddRange(tokens2); + return default; } - if (literal3 is { } tokens3) - { - builder.AddRange(tokens3); - } + using var builder = new PooledArrayBuilder(count); - if (literal4 is { } tokens4) + foreach (var tokenList in tokenLists) { - builder.AddRange(tokens4); - } + if (tokenList == null) + { + continue; + } - if (literal5 is { } tokens5) - { - builder.AddRange(tokens5); + builder.AddRange(tokenList.GetValueOrDefault()); } return builder.ToList(); @@ -574,7 +566,10 @@ public bool TryCast(out ImmutableArray result) protected static MarkupTextLiteralSyntax MergeAttributeValue(MarkupLiteralAttributeValueSyntax node) { - var valueTokens = MergeLiterals(node.Prefix?.LiteralTokens, node.Value?.LiteralTokens); + var valueTokens = MergeTokenLists( + node.Prefix?.LiteralTokens, + node.Value?.LiteralTokens); + var rewritten = node.Prefix?.Update(valueTokens, node.Prefix.ChunkGenerator) ?? node.Value?.Update(valueTokens, node.Value.ChunkGenerator); rewritten = (MarkupTextLiteralSyntax)rewritten?.Green.CreateRed(node, node.Position); @@ -606,12 +601,13 @@ public LegacyFileKindVisitor(DocumentIntermediateNode document, IntermediateNode // Suffix=" public override void VisitMarkupAttributeBlock(MarkupAttributeBlockSyntax node) { - var prefixTokens = MergeLiterals( + var prefixTokens = MergeTokenLists( node.NamePrefix?.LiteralTokens, node.Name.LiteralTokens, node.NameSuffix?.LiteralTokens, - node.EqualsToken != null ? new SyntaxList(node.EqualsToken) : default, + new SyntaxList(node.EqualsToken), node.ValuePrefix?.LiteralTokens); + var prefix = (MarkupTextLiteralSyntax)SyntaxFactory.MarkupTextLiteral(prefixTokens, chunkGenerator: null).Green.CreateRed(node, node.NamePrefix?.Position ?? node.Name.Position); var name = node.Name.GetContent(); @@ -629,7 +625,7 @@ public override void VisitMarkupAttributeBlock(MarkupAttributeBlockSyntax node) if (children.TryCast(out var attributeLiteralArray)) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using var builder = new PooledArrayBuilder(); foreach (var literal in attributeLiteralArray) { @@ -639,7 +635,11 @@ public override void VisitMarkupAttributeBlock(MarkupAttributeBlockSyntax node) var rewritten = SyntaxFactory.MarkupTextLiteral(builder.ToList(), chunkGenerator: null); - var mergedLiterals = MergeLiterals(prefix?.LiteralTokens, rewritten.LiteralTokens, node.ValueSuffix?.LiteralTokens); + var mergedLiterals = MergeTokenLists( + prefix?.LiteralTokens, + rewritten.LiteralTokens, + node.ValueSuffix?.LiteralTokens); + var mergedAttribute = SyntaxFactory.MarkupTextLiteral(mergedLiterals, chunkGenerator: null).Green.CreateRed(node.Parent, node.Position); Visit(mergedAttribute); @@ -675,9 +675,10 @@ public override void VisitMarkupMinimizedAttributeBlock(MarkupMinimizedAttribute } // Minimized attributes are just html content. - var literals = MergeLiterals( + var literals = MergeTokenLists( node.NamePrefix?.LiteralTokens, node.Name?.LiteralTokens); + var literal = SyntaxFactory.MarkupTextLiteral(literals, chunkGenerator: null).Green.CreateRed(node.Parent, node.Position); Visit(literal); @@ -1188,7 +1189,7 @@ private void VisitAttributeValue(SyntaxNode node) if (children.TryCast(out var attributeLiteralArray)) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; foreach (var literal in attributeLiteralArray) { @@ -1201,7 +1202,7 @@ private void VisitAttributeValue(SyntaxNode node) } else if (children.TryCast(out var markupLiteralArray)) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; foreach (var literal in markupLiteralArray) { @@ -1213,7 +1214,7 @@ private void VisitAttributeValue(SyntaxNode node) } else if (children.TryCast(out var expressionLiteralArray)) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; SpanEditHandler editHandler = null; ISpanChunkGenerator generator = null; @@ -1244,16 +1245,16 @@ private void Combine(HtmlContentIntermediateNode node, SyntaxNode item) Source = BuildSourceSpanFromNode(item), }); - if (node.Source != null) + if (node.Source is SourceSpan source) { node.Source = new SourceSpan( - node.Source.Value.FilePath, - node.Source.Value.AbsoluteIndex, - node.Source.Value.LineIndex, - node.Source.Value.CharacterIndex, - node.Source.Value.Length + item.Width, - node.Source.Value.LineCount, - node.Source.Value.EndCharacterIndex); + source.FilePath, + source.AbsoluteIndex, + source.LineIndex, + source.CharacterIndex, + source.Length + item.Width, + source.LineCount, + source.EndCharacterIndex); } } } @@ -1298,8 +1299,8 @@ public override void VisitMarkupElement(MarkupElementSyntax node) if (node.StartTag != null && node.EndTag != null && node.StartTag.IsVoidElement()) { element.Diagnostics.Add( - ComponentDiagnosticFactory.Create_UnexpectedClosingTagForVoidElement( - BuildSourceSpanFromNode(node.EndTag), node.EndTag.GetTagNameWithOptionalBang())); + ComponentDiagnosticFactory.Create_UnexpectedClosingTagForVoidElement( + BuildSourceSpanFromNode(node.EndTag), node.EndTag.GetTagNameWithOptionalBang())); } else if (node.StartTag != null && node.EndTag == null && !node.StartTag.IsVoidElement() && !node.StartTag.IsSelfClosing()) { @@ -1388,12 +1389,13 @@ public override void VisitMarkupAttributeBlock(MarkupAttributeBlockSyntax node) // building Prefix and Suffix, even though we don't really use them. If we // end up using another node type in the future this can be simplified quite // a lot. - var prefixTokens = MergeLiterals( + var prefixTokens = MergeTokenLists( node.NamePrefix?.LiteralTokens, node.Name.LiteralTokens, node.NameSuffix?.LiteralTokens, - node.EqualsToken == null ? new SyntaxList() : new SyntaxList(node.EqualsToken), + new SyntaxList(node.EqualsToken), node.ValuePrefix?.LiteralTokens); + var prefix = (MarkupTextLiteralSyntax)SyntaxFactory.MarkupTextLiteral(prefixTokens, chunkGenerator: null).Green.CreateRed(node, node.NamePrefix?.Position ?? node.Name.Position); var name = node.Name.GetContent(); @@ -1412,7 +1414,10 @@ public override void VisitMarkupAttributeBlock(MarkupAttributeBlockSyntax node) public override void VisitMarkupMinimizedAttributeBlock(MarkupMinimizedAttributeBlockSyntax node) { - var prefixTokens = MergeLiterals(node.NamePrefix?.LiteralTokens, node.Name.LiteralTokens); + var prefixTokens = MergeTokenLists( + node.NamePrefix?.LiteralTokens, + node.Name.LiteralTokens); + var prefix = (MarkupTextLiteralSyntax)SyntaxFactory.MarkupTextLiteral(prefixTokens, chunkGenerator: null).Green.CreateRed(node, node.NamePrefix?.Position ?? node.Name.Position); var name = node.Name.GetContent(); @@ -2158,7 +2163,7 @@ private void VisitAttributeValue(SyntaxNode node) if (children.TryCast(out var attributeLiteralArray)) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var valueTokens); + using PooledArrayBuilder valueTokens = []; foreach (var literal in attributeLiteralArray) { @@ -2171,7 +2176,7 @@ private void VisitAttributeValue(SyntaxNode node) } else if (children.TryCast(out var markupLiteralArray)) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; foreach (var literal in markupLiteralArray) { @@ -2183,7 +2188,7 @@ private void VisitAttributeValue(SyntaxNode node) } else if (children.TryCast(out var expressionLiteralArray)) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; ISpanChunkGenerator generator = null; SpanEditHandler editHandler = null; @@ -2195,7 +2200,12 @@ private void VisitAttributeValue(SyntaxNode node) } var rewritten = SyntaxFactory.CSharpExpressionLiteral(builder.ToList(), generator).Green.CreateRed(node.Parent, position); - rewritten = editHandler != null ? rewritten.WithEditHandler(editHandler) : rewritten; + + if (editHandler != null) + { + rewritten = rewritten.WithEditHandler(editHandler); + } + Visit(rewritten); } else diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/LegacySyntaxNodeExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/LegacySyntaxNodeExtensions.cs index 8e6f8583c09..716cb2f659e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/LegacySyntaxNodeExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/LegacySyntaxNodeExtensions.cs @@ -164,7 +164,7 @@ public static TNode WithEditHandler(this TNode node, SpanEditHandler? edi if (node.IsSpanKind()) { - var editHandler = node.GetEditHandler() ?? SpanEditHandler.CreateDefault(AcceptedCharactersInternal.Any); + var editHandler = node.GetEditHandler() ?? SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any); return editHandler.OwnsChange(node, change) ? node : null; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandler.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandler.cs index 2ace47905db..c11f98ce705 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandler.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandler.cs @@ -5,22 +5,43 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using Microsoft.AspNetCore.Razor.Language.Syntax; namespace Microsoft.AspNetCore.Razor.Language.Legacy; internal class SpanEditHandler { + internal static readonly Func> NoTokenizer = _ => []; + + private static readonly ImmutableArray s_defaultEditHandlers = + [ + // AcceptedCharactersInternal consists up of 3 bit flags. + // So, there are 8 possible combinations from 0 to 7. + CreateDefault(NoTokenizer, AcceptedCharactersInternal.None), + CreateDefault(NoTokenizer, (AcceptedCharactersInternal)1), + CreateDefault(NoTokenizer, (AcceptedCharactersInternal)2), + CreateDefault(NoTokenizer, (AcceptedCharactersInternal)3), + CreateDefault(NoTokenizer, (AcceptedCharactersInternal)4), + CreateDefault(NoTokenizer, (AcceptedCharactersInternal)5), + CreateDefault(NoTokenizer, (AcceptedCharactersInternal)6), + CreateDefault(NoTokenizer, (AcceptedCharactersInternal)7) + ]; + private static readonly int TypeHashCode = typeof(SpanEditHandler).GetHashCode(); public required AcceptedCharactersInternal AcceptedCharacters { get; init; } public required Func> Tokenizer { get; init; } - public static SpanEditHandler CreateDefault(AcceptedCharactersInternal acceptedCharacters) + public static SpanEditHandler GetDefault(AcceptedCharactersInternal acceptedCharacters) { - return CreateDefault(static c => Enumerable.Empty(), acceptedCharacters); + var index = (int)acceptedCharacters; + + ArgHelper.ThrowIfNegative(index, nameof(acceptedCharacters)); + ArgHelper.ThrowIfGreaterThanOrEqual(index, 8, nameof(acceptedCharacters)); + + return s_defaultEditHandlers[index]; } public static SpanEditHandler CreateDefault(Func> tokenizer, AcceptedCharactersInternal acceptedCharacters) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandlerBuilder.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandlerBuilder.cs index 302f7beb029..cc5bbab1249 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandlerBuilder.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/SpanEditHandlerBuilder.cs @@ -1,18 +1,17 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax; namespace Microsoft.AspNetCore.Razor.Language.Legacy; internal sealed class SpanEditHandlerBuilder { - private static readonly Func> DefaultTokenizer = static content => Enumerable.Empty(); - private static readonly SpanEditHandler DefaultEditHandler = SpanEditHandler.CreateDefault(AcceptedCharactersInternal.Any); + private static readonly Func> DefaultTokenizer = SpanEditHandler.NoTokenizer; + private static readonly SpanEditHandler DefaultEditHandler = SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any); private readonly Func>? _defaultLanguageTokenizer; private readonly SpanEditHandler? _defaultLanguageEditHandler; diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs index 0531afa4b82..7731498b812 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs @@ -8,6 +8,7 @@ using System.Linq; using Microsoft.AspNetCore.Razor.Language.Components; using Microsoft.AspNetCore.Razor.Language.Syntax; +using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language.Legacy; @@ -66,8 +67,9 @@ public static MarkupTagHelperStartTagSyntax Rewrite( { var processedBoundAttributeNames = new HashSet(StringComparer.OrdinalIgnoreCase); + using PooledArrayBuilder attributeBuilder = []; + var attributes = startTag.Attributes; - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var attributeBuilder); for (var i = 0; i < startTag.Attributes.Count; i++) { @@ -262,10 +264,10 @@ private static TryParseResult TryParseAttribute( var attributeValue = attributeBlock.Value; if (attributeValue == null) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; // Add a marker for attribute value when there are no quotes like,

- builder.Add(SyntaxFactory.MarkupTextLiteral(new SyntaxList(), chunkGenerator: null)); + builder.Add(SyntaxFactory.MarkupTextLiteral(literalTokens: default, chunkGenerator: null)); attributeValue = SyntaxFactory.GenericBlock(builder.ToList()); } @@ -608,7 +610,7 @@ public override SyntaxNode VisitCSharpImplicitExpression(CSharpImplicitExpressio { if (_rewriteAsMarkup) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; var rewrittenBody = (CSharpCodeBlockSyntax)VisitCSharpCodeBlock(((CSharpImplicitExpressionBodySyntax)node.Body).CSharpCode); @@ -634,8 +636,8 @@ public override SyntaxNode VisitCSharpImplicitExpression(CSharpImplicitExpressio public override SyntaxNode VisitCSharpExplicitExpression(CSharpExplicitExpressionSyntax node) { - CSharpTransitionSyntax transition = null; - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + CSharpTransitionSyntax transition; + using PooledArrayBuilder builder = []; if (_rewriteAsMarkup) { @@ -643,7 +645,7 @@ public override SyntaxNode VisitCSharpExplicitExpression(CSharpExplicitExpressio // Change to a MarkupChunkGenerator so that the '@' \ parenthesis is generated as part of the output. // This is bad code, since @( is never valid C#, so we don't worry about trying to stitch the @ and the ( together. var editHandler = _options.EnableSpanEditHandlers - ? node.GetEditHandler() ?? SpanEditHandler.CreateDefault((content) => Enumerable.Empty(), AcceptedCharactersInternal.Any) + ? node.GetEditHandler() ?? SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any) : null; var expression = SyntaxFactory.CSharpExpressionLiteral(new SyntaxList(node.Transition.Transition), MarkupChunkGenerator.Instance).WithEditHandler(editHandler); @@ -736,7 +738,7 @@ public override SyntaxNode VisitCSharpExpressionLiteral(CSharpExpressionLiteralS public override SyntaxNode VisitMarkupLiteralAttributeValue(MarkupLiteralAttributeValueSyntax node) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; if (node.Prefix != null) { @@ -768,7 +770,7 @@ public override SyntaxNode VisitMarkupLiteralAttributeValue(MarkupLiteralAttribu public override SyntaxNode VisitMarkupDynamicAttributeValue(MarkupDynamicAttributeValueSyntax node) { // Move the prefix to be part of the actual value. - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; if (node.Prefix != null) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperParseTreeRewriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperParseTreeRewriter.cs index 8e4a823c39c..dcc5167dd70 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperParseTreeRewriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperParseTreeRewriter.cs @@ -118,7 +118,8 @@ public override SyntaxNode VisitMarkupElement(MarkupElementSyntax node) } // This tag contains a body and/or an end tag which needs to be moved to the parent. - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var rewrittenNodes); + using PooledArrayBuilder rewrittenNodes = []; + rewrittenNodes.Add(rewrittenTagHelper); var rewrittenBody = VisitList(node.Body); rewrittenNodes.AddRange(rewrittenBody); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/BaseMarkupEndTagSyntax.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/BaseMarkupEndTagSyntax.cs new file mode 100644 index 00000000000..78e02a2584d --- /dev/null +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/BaseMarkupEndTagSyntax.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Razor.Language.Legacy; +using Microsoft.AspNetCore.Razor.PooledObjects; + +namespace Microsoft.AspNetCore.Razor.Language.Syntax; + +internal abstract partial class BaseMarkupEndTagSyntax +{ + private SyntaxNode? _lazyChildren; + + public SyntaxList LegacyChildren + { + get + { + var children = _lazyChildren ?? + InterlockedOperations.Initialize(ref _lazyChildren, this.ComputeEndTagLegacyChildren()); + + return new SyntaxList(children); + } + } + + private SyntaxNode ComputeEndTagLegacyChildren() + { + // This method returns the children of this end tag in legacy format. + // This is needed to generate the same classified spans as the legacy syntax tree. + + using PooledArrayBuilder builder = []; + using PooledArrayBuilder tokensBuilder = []; + + // Take a ref to tokensBuilder here to avoid calling AsRef() multiple times below + // for each call to ToListAndClear(). + ref var tokens = ref tokensBuilder.AsRef(); + + var editHandler = this.GetEditHandler(); + var chunkGenerator = ChunkGenerator; + + if (OpenAngle is { IsMissing: false } openAngle) + { + tokens.Add(openAngle); + } + + if (ForwardSlash is { IsMissing: false } forwardSlash) + { + tokens.Add(forwardSlash); + } + + if (Bang is { IsMissing: false } bang) + { + SpanEditHandler? acceptsAnyHandler = null; + SpanEditHandler? acceptsNoneHandler = null; + + if (editHandler != null) + { + acceptsAnyHandler = SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any); + acceptsNoneHandler = SpanEditHandler.GetDefault(AcceptedCharactersInternal.None); + } + + // The prefix of an end tag(E.g '|') will have 'Any' accepted characters if a bang exists. + builder.Add(MarkupTextLiteral(tokens.ToListAndClear(), chunkGenerator, acceptsAnyHandler)); + + // We can skip adding bang to the tokens builder, since we just cleared it. + builder.Add(RazorMetaCode(bang, chunkGenerator, acceptsNoneHandler)); + } + + if (Name is { IsMissing: false } name) + { + tokens.Add(name); + } + + if (MiscAttributeContent?.Children is { Count: > 0 } children) + { + foreach (var content in children) + { + var literal = (MarkupTextLiteralSyntax)content; + tokens.AddRange(literal.LiteralTokens); + } + } + + if (CloseAngle is { IsMissing: false } closeAngle) + { + tokens.Add(closeAngle); + } + + builder.Add(MarkupTextLiteral(tokens.ToListAndClear(), chunkGenerator, editHandler)); + + return builder.ToListNode(parent: this, Position) + .AssumeNotNull($"ToListNode should not return null since builder was not empty."); + + + static MarkupTextLiteralSyntax MarkupTextLiteral( + SyntaxList tokens, ISpanChunkGenerator? chunkGenerator, SpanEditHandler? editHandler) + { + var node = SyntaxFactory.MarkupTextLiteral(tokens, chunkGenerator); + + if (editHandler != null) + { + node = node.WithEditHandler(editHandler); + } + + return node; + } + + static RazorMetaCodeSyntax RazorMetaCode( + SyntaxToken token, ISpanChunkGenerator? chunkGenerator, SpanEditHandler? editHandler) + { + var node = SyntaxFactory.RazorMetaCode(token, chunkGenerator); + + if (editHandler != null) + { + node = node.WithEditHandler(editHandler); + } + + return node; + } + } +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/BaseMarkupStartTagSyntax.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/BaseMarkupStartTagSyntax.cs new file mode 100644 index 00000000000..0d604cd5c7b --- /dev/null +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/BaseMarkupStartTagSyntax.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Razor.Language.Legacy; +using Microsoft.AspNetCore.Razor.PooledObjects; + +namespace Microsoft.AspNetCore.Razor.Language.Syntax; + +internal abstract partial class BaseMarkupStartTagSyntax +{ + private SyntaxNode? _lazyChildren; + + public SyntaxList LegacyChildren + { + get + { + var children = _lazyChildren ?? + InterlockedOperations.Initialize(ref _lazyChildren, ComputeStartTagLegacyChildren()); + + return new SyntaxList(children); + } + } + + ///

+ /// This method returns the children of this start tag in legacy format. + /// This is needed to generate the same classified spans as the legacy syntax tree. + /// + private SyntaxNode ComputeStartTagLegacyChildren() + { + using PooledArrayBuilder builder = []; + using PooledArrayBuilder tokensBuilder = []; + + // Take a ref to tokensBuilder here to avoid calling AsRef() multiple times below + // for each call to ToListAndClear(). + ref var tokens = ref tokensBuilder.AsRef(); + + SpanEditHandler? acceptsAnyHandler = null; + SpanEditHandler? acceptsNoneHandler = null; + + var containsAttributesContent = false; + + var editHandler = this.GetEditHandler(); + if (editHandler != null) + { + acceptsAnyHandler = SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any); + acceptsNoneHandler = SpanEditHandler.GetDefault(AcceptedCharactersInternal.None); + + // We want to know if this tag contains non-whitespace attribute content to set + // the appropriate AcceptedCharacters. The prefix of a start tag(E.g '|') + // will have 'Any' accepted characters if non-whitespace attribute content exists. + + foreach (var attribute in Attributes) + { + foreach (var token in attribute.DescendantTokens()) + { + if (!string.IsNullOrWhiteSpace(token.Content)) + { + containsAttributesContent = true; + break; + } + } + } + } + + var chunkGenerator = ChunkGenerator; + + if (OpenAngle is { IsMissing: false } openAngle) + { + tokens.Add(openAngle); + } + + if (Bang is { IsMissing: false } bang) + { + builder.Add(MarkupTextLiteral(tokens.ToListAndClear(), chunkGenerator, acceptsAnyHandler)); + + // We can skip adding bang to the tokens builder, since we just cleared it. + builder.Add(RazorMetaCode(bang, chunkGenerator, acceptsNoneHandler)); + } + + if (Name is { IsMissing: false } name) + { + tokens.Add(name); + } + + builder.Add(MarkupTextLiteral( + tokens.ToListAndClear(), chunkGenerator, containsAttributesContent ? acceptsAnyHandler : editHandler)); + + builder.AddRange(Attributes); + + if (ForwardSlash is { IsMissing: false } forwardSlash) + { + tokens.Add(forwardSlash); + } + + if (CloseAngle is { IsMissing: false } closeAngle) + { + tokens.Add(closeAngle); + } + + if (tokens.Count > 0) + { + builder.Add(MarkupTextLiteral(tokens.ToListAndClear(), chunkGenerator, editHandler)); + } + + return builder.ToListNode(parent: this, Position) + .AssumeNotNull($"ToListNode should not return null since builder was not empty."); + + static MarkupTextLiteralSyntax MarkupTextLiteral( + SyntaxList tokens, ISpanChunkGenerator? chunkGenerator, SpanEditHandler? editHandler) + { + var node = SyntaxFactory.MarkupTextLiteral(tokens, chunkGenerator); + + if (editHandler != null) + { + node = node.WithEditHandler(editHandler); + } + + return node; + } + + static RazorMetaCodeSyntax RazorMetaCode( + SyntaxToken token, ISpanChunkGenerator? chunkGenerator, SpanEditHandler? editHandler) + { + var node = SyntaxFactory.RazorMetaCode(token, chunkGenerator); + + if (editHandler != null) + { + node = node.WithEditHandler(editHandler); + } + + return node; + } + } +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Internal.Generated.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Internal.Generated.cs index 90bbbd9b49a..ec47c306fb1 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Internal.Generated.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Internal.Generated.cs @@ -1094,6 +1094,60 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) => new MarkupDynamicAttributeValueSyntax(Kind, _prefix, _value, GetDiagnostics(), annotations); } +internal abstract partial class BaseMarkupStartTagSyntax : MarkupSyntaxNode +{ + internal BaseMarkupStartTagSyntax(SyntaxKind kind, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations) + : base(kind, diagnostics, annotations) + { + } + + internal BaseMarkupStartTagSyntax(SyntaxKind kind) + : base(kind) + { + } + + public abstract SyntaxToken OpenAngle { get; } + + public abstract SyntaxToken Bang { get; } + + public abstract SyntaxToken Name { get; } + + public abstract SyntaxList Attributes { get; } + + public abstract SyntaxToken ForwardSlash { get; } + + public abstract SyntaxToken CloseAngle { get; } + + public abstract ISpanChunkGenerator ChunkGenerator { get; } +} + +internal abstract partial class BaseMarkupEndTagSyntax : MarkupSyntaxNode +{ + internal BaseMarkupEndTagSyntax(SyntaxKind kind, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations) + : base(kind, diagnostics, annotations) + { + } + + internal BaseMarkupEndTagSyntax(SyntaxKind kind) + : base(kind) + { + } + + public abstract SyntaxToken OpenAngle { get; } + + public abstract SyntaxToken ForwardSlash { get; } + + public abstract SyntaxToken Bang { get; } + + public abstract SyntaxToken Name { get; } + + public abstract MarkupMiscAttributeContentSyntax MiscAttributeContent { get; } + + public abstract SyntaxToken CloseAngle { get; } + + public abstract ISpanChunkGenerator ChunkGenerator { get; } +} + internal sealed partial class MarkupElementSyntax : MarkupSyntaxNode { private readonly MarkupStartTagSyntax _startTag; @@ -1184,7 +1238,7 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) => new MarkupElementSyntax(Kind, _startTag, _body, _endTag, GetDiagnostics(), annotations); } -internal sealed partial class MarkupStartTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupStartTagSyntax : BaseMarkupStartTagSyntax { private readonly SyntaxToken _openAngle; private readonly SyntaxToken _bang; @@ -1250,13 +1304,13 @@ internal MarkupStartTagSyntax(SyntaxKind kind, SyntaxToken openAngle, SyntaxToke _chunkGenerator = chunkGenerator; } - public SyntaxToken OpenAngle => _openAngle; - public SyntaxToken Bang => _bang; - public SyntaxToken Name => _name; - public SyntaxList Attributes => new SyntaxList(_attributes); - public SyntaxToken ForwardSlash => _forwardSlash; - public SyntaxToken CloseAngle => _closeAngle; - public ISpanChunkGenerator ChunkGenerator => _chunkGenerator; + public override SyntaxToken OpenAngle => _openAngle; + public override SyntaxToken Bang => _bang; + public override SyntaxToken Name => _name; + public override SyntaxList Attributes => new SyntaxList(_attributes); + public override SyntaxToken ForwardSlash => _forwardSlash; + public override SyntaxToken CloseAngle => _closeAngle; + public override ISpanChunkGenerator ChunkGenerator => _chunkGenerator; internal override GreenNode GetSlot(int index) => index switch @@ -1299,7 +1353,7 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) => new MarkupStartTagSyntax(Kind, _openAngle, _bang, _name, _attributes, _forwardSlash, _closeAngle, _chunkGenerator, GetDiagnostics(), annotations); } -internal sealed partial class MarkupEndTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupEndTagSyntax : BaseMarkupEndTagSyntax { private readonly SyntaxToken _openAngle; private readonly SyntaxToken _forwardSlash; @@ -1359,13 +1413,13 @@ internal MarkupEndTagSyntax(SyntaxKind kind, SyntaxToken openAngle, SyntaxToken _chunkGenerator = chunkGenerator; } - public SyntaxToken OpenAngle => _openAngle; - public SyntaxToken ForwardSlash => _forwardSlash; - public SyntaxToken Bang => _bang; - public SyntaxToken Name => _name; - public MarkupMiscAttributeContentSyntax MiscAttributeContent => _miscAttributeContent; - public SyntaxToken CloseAngle => _closeAngle; - public ISpanChunkGenerator ChunkGenerator => _chunkGenerator; + public override SyntaxToken OpenAngle => _openAngle; + public override SyntaxToken ForwardSlash => _forwardSlash; + public override SyntaxToken Bang => _bang; + public override SyntaxToken Name => _name; + public override MarkupMiscAttributeContentSyntax MiscAttributeContent => _miscAttributeContent; + public override SyntaxToken CloseAngle => _closeAngle; + public override ISpanChunkGenerator ChunkGenerator => _chunkGenerator; internal override GreenNode GetSlot(int index) => index switch @@ -1492,7 +1546,7 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) => new MarkupTagHelperElementSyntax(Kind, _startTag, _body, _endTag, GetDiagnostics(), annotations); } -internal sealed partial class MarkupTagHelperStartTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupTagHelperStartTagSyntax : BaseMarkupStartTagSyntax { private readonly SyntaxToken _openAngle; private readonly SyntaxToken _bang; @@ -1558,13 +1612,13 @@ internal MarkupTagHelperStartTagSyntax(SyntaxKind kind, SyntaxToken openAngle, S _chunkGenerator = chunkGenerator; } - public SyntaxToken OpenAngle => _openAngle; - public SyntaxToken Bang => _bang; - public SyntaxToken Name => _name; - public SyntaxList Attributes => new SyntaxList(_attributes); - public SyntaxToken ForwardSlash => _forwardSlash; - public SyntaxToken CloseAngle => _closeAngle; - public ISpanChunkGenerator ChunkGenerator => _chunkGenerator; + public override SyntaxToken OpenAngle => _openAngle; + public override SyntaxToken Bang => _bang; + public override SyntaxToken Name => _name; + public override SyntaxList Attributes => new SyntaxList(_attributes); + public override SyntaxToken ForwardSlash => _forwardSlash; + public override SyntaxToken CloseAngle => _closeAngle; + public override ISpanChunkGenerator ChunkGenerator => _chunkGenerator; internal override GreenNode GetSlot(int index) => index switch @@ -1607,7 +1661,7 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations) => new MarkupTagHelperStartTagSyntax(Kind, _openAngle, _bang, _name, _attributes, _forwardSlash, _closeAngle, _chunkGenerator, GetDiagnostics(), annotations); } -internal sealed partial class MarkupTagHelperEndTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupTagHelperEndTagSyntax : BaseMarkupEndTagSyntax { private readonly SyntaxToken _openAngle; private readonly SyntaxToken _forwardSlash; @@ -1667,13 +1721,13 @@ internal MarkupTagHelperEndTagSyntax(SyntaxKind kind, SyntaxToken openAngle, Syn _chunkGenerator = chunkGenerator; } - public SyntaxToken OpenAngle => _openAngle; - public SyntaxToken ForwardSlash => _forwardSlash; - public SyntaxToken Bang => _bang; - public SyntaxToken Name => _name; - public MarkupMiscAttributeContentSyntax MiscAttributeContent => _miscAttributeContent; - public SyntaxToken CloseAngle => _closeAngle; - public ISpanChunkGenerator ChunkGenerator => _chunkGenerator; + public override SyntaxToken OpenAngle => _openAngle; + public override SyntaxToken ForwardSlash => _forwardSlash; + public override SyntaxToken Bang => _bang; + public override SyntaxToken Name => _name; + public override MarkupMiscAttributeContentSyntax MiscAttributeContent => _miscAttributeContent; + public override SyntaxToken CloseAngle => _closeAngle; + public override ISpanChunkGenerator ChunkGenerator => _chunkGenerator; internal override GreenNode GetSlot(int index) => index switch diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Syntax.Generated.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Syntax.Generated.cs index 6047d80bbbd..97025fb3030 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Syntax.Generated.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Generated/Syntax.xml.Syntax.Generated.cs @@ -737,6 +737,80 @@ public MarkupDynamicAttributeValueSyntax Update(MarkupTextLiteralSyntax prefix, public MarkupDynamicAttributeValueSyntax WithValue(RazorBlockSyntax value) => Update(Prefix, value); } +internal abstract partial class BaseMarkupStartTagSyntax : MarkupSyntaxNode +{ + internal BaseMarkupStartTagSyntax(GreenNode green, SyntaxNode parent, int position) + : base(green, parent, position) + { + } + + public abstract SyntaxToken OpenAngle { get; } + public BaseMarkupStartTagSyntax WithOpenAngle(SyntaxToken openAngle) => WithOpenAngleCore(openAngle); + internal abstract BaseMarkupStartTagSyntax WithOpenAngleCore(SyntaxToken openAngle); + + public abstract SyntaxToken Bang { get; } + public BaseMarkupStartTagSyntax WithBang(SyntaxToken bang) => WithBangCore(bang); + internal abstract BaseMarkupStartTagSyntax WithBangCore(SyntaxToken bang); + + public abstract SyntaxToken Name { get; } + public BaseMarkupStartTagSyntax WithName(SyntaxToken name) => WithNameCore(name); + internal abstract BaseMarkupStartTagSyntax WithNameCore(SyntaxToken name); + + public abstract SyntaxList Attributes { get; } + public BaseMarkupStartTagSyntax WithAttributes(SyntaxList attributes) => WithAttributesCore(attributes); + internal abstract BaseMarkupStartTagSyntax WithAttributesCore(SyntaxList attributes); + + public BaseMarkupStartTagSyntax AddAttributes(params RazorSyntaxNode[] items) => AddAttributesCore(items); + internal abstract BaseMarkupStartTagSyntax AddAttributesCore(params RazorSyntaxNode[] items); + + public abstract SyntaxToken ForwardSlash { get; } + public BaseMarkupStartTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => WithForwardSlashCore(forwardSlash); + internal abstract BaseMarkupStartTagSyntax WithForwardSlashCore(SyntaxToken forwardSlash); + + public abstract SyntaxToken CloseAngle { get; } + public BaseMarkupStartTagSyntax WithCloseAngle(SyntaxToken closeAngle) => WithCloseAngleCore(closeAngle); + internal abstract BaseMarkupStartTagSyntax WithCloseAngleCore(SyntaxToken closeAngle); + + public abstract ISpanChunkGenerator ChunkGenerator { get; } +} + +internal abstract partial class BaseMarkupEndTagSyntax : MarkupSyntaxNode +{ + internal BaseMarkupEndTagSyntax(GreenNode green, SyntaxNode parent, int position) + : base(green, parent, position) + { + } + + public abstract SyntaxToken OpenAngle { get; } + public BaseMarkupEndTagSyntax WithOpenAngle(SyntaxToken openAngle) => WithOpenAngleCore(openAngle); + internal abstract BaseMarkupEndTagSyntax WithOpenAngleCore(SyntaxToken openAngle); + + public abstract SyntaxToken ForwardSlash { get; } + public BaseMarkupEndTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => WithForwardSlashCore(forwardSlash); + internal abstract BaseMarkupEndTagSyntax WithForwardSlashCore(SyntaxToken forwardSlash); + + public abstract SyntaxToken Bang { get; } + public BaseMarkupEndTagSyntax WithBang(SyntaxToken bang) => WithBangCore(bang); + internal abstract BaseMarkupEndTagSyntax WithBangCore(SyntaxToken bang); + + public abstract SyntaxToken Name { get; } + public BaseMarkupEndTagSyntax WithName(SyntaxToken name) => WithNameCore(name); + internal abstract BaseMarkupEndTagSyntax WithNameCore(SyntaxToken name); + + public abstract MarkupMiscAttributeContentSyntax MiscAttributeContent { get; } + public BaseMarkupEndTagSyntax WithMiscAttributeContent(MarkupMiscAttributeContentSyntax miscAttributeContent) => WithMiscAttributeContentCore(miscAttributeContent); + internal abstract BaseMarkupEndTagSyntax WithMiscAttributeContentCore(MarkupMiscAttributeContentSyntax miscAttributeContent); + + public BaseMarkupEndTagSyntax AddMiscAttributeContentChildren(params RazorSyntaxNode[] items) => AddMiscAttributeContentChildrenCore(items); + internal abstract BaseMarkupEndTagSyntax AddMiscAttributeContentChildrenCore(params RazorSyntaxNode[] items); + + public abstract SyntaxToken CloseAngle { get; } + public BaseMarkupEndTagSyntax WithCloseAngle(SyntaxToken closeAngle) => WithCloseAngleCore(closeAngle); + internal abstract BaseMarkupEndTagSyntax WithCloseAngleCore(SyntaxToken closeAngle); + + public abstract ISpanChunkGenerator ChunkGenerator { get; } +} + internal sealed partial class MarkupElementSyntax : MarkupSyntaxNode { private MarkupStartTagSyntax _startTag; @@ -795,7 +869,7 @@ public MarkupElementSyntax Update(MarkupStartTagSyntax startTag, SyntaxList WithBody(this.Body.AddRange(items)); } -internal sealed partial class MarkupStartTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupStartTagSyntax : BaseMarkupStartTagSyntax { private SyntaxToken _openAngle; private SyntaxToken _bang; @@ -809,13 +883,13 @@ internal MarkupStartTagSyntax(GreenNode green, SyntaxNode parent, int position) { } - public SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); - public SyntaxToken Bang => GetRed(ref _bang, 1); - public SyntaxToken Name => GetRed(ref _name, 2); - public SyntaxList Attributes => new SyntaxList(GetRed(ref _attributes, 3)); - public SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 4); - public SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); - public ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupStartTagSyntax)Green).ChunkGenerator; + public override SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); + public override SyntaxToken Bang => GetRed(ref _bang, 1); + public override SyntaxToken Name => GetRed(ref _name, 2); + public override SyntaxList Attributes => new SyntaxList(GetRed(ref _attributes, 3)); + public override SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 4); + public override SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); + public override ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupStartTagSyntax)Green).ChunkGenerator; internal override SyntaxNode GetNodeSlot(int index) => index switch @@ -859,18 +933,25 @@ public MarkupStartTagSyntax Update(SyntaxToken openAngle, SyntaxToken bang, Synt return this; } - public MarkupStartTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, Bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupStartTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupStartTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, Bang, name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupStartTagSyntax WithAttributes(SyntaxList attributes) => Update(OpenAngle, Bang, Name, attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupStartTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, Bang, Name, Attributes, forwardSlash, CloseAngle, ChunkGenerator); - public MarkupStartTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, Bang, Name, Attributes, ForwardSlash, closeAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithOpenAngleCore(SyntaxToken openAngle) => WithOpenAngle(openAngle); + public new MarkupStartTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, Bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithBangCore(SyntaxToken bang) => WithBang(bang); + public new MarkupStartTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithNameCore(SyntaxToken name) => WithName(name); + public new MarkupStartTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, Bang, name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithAttributesCore(SyntaxList attributes) => WithAttributes(attributes); + public new MarkupStartTagSyntax WithAttributes(SyntaxList attributes) => Update(OpenAngle, Bang, Name, attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithForwardSlashCore(SyntaxToken forwardSlash) => WithForwardSlash(forwardSlash); + public new MarkupStartTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, Bang, Name, Attributes, forwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithCloseAngleCore(SyntaxToken closeAngle) => WithCloseAngle(closeAngle); + public new MarkupStartTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, Bang, Name, Attributes, ForwardSlash, closeAngle, ChunkGenerator); public MarkupStartTagSyntax WithChunkGenerator(ISpanChunkGenerator chunkGenerator) => Update(OpenAngle, Bang, Name, Attributes, ForwardSlash, CloseAngle, chunkGenerator); + internal override BaseMarkupStartTagSyntax AddAttributesCore(params RazorSyntaxNode[] items) => AddAttributes(items); - public MarkupStartTagSyntax AddAttributes(params RazorSyntaxNode[] items) => WithAttributes(this.Attributes.AddRange(items)); + public new MarkupStartTagSyntax AddAttributes(params RazorSyntaxNode[] items) => WithAttributes(this.Attributes.AddRange(items)); } -internal sealed partial class MarkupEndTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupEndTagSyntax : BaseMarkupEndTagSyntax { private SyntaxToken _openAngle; private SyntaxToken _forwardSlash; @@ -884,13 +965,13 @@ internal MarkupEndTagSyntax(GreenNode green, SyntaxNode parent, int position) { } - public SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); - public SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 1); - public SyntaxToken Bang => GetRed(ref _bang, 2); - public SyntaxToken Name => GetRed(ref _name, 3); - public MarkupMiscAttributeContentSyntax MiscAttributeContent => GetRed(ref _miscAttributeContent, 4); - public SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); - public ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupEndTagSyntax)Green).ChunkGenerator; + public override SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); + public override SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 1); + public override SyntaxToken Bang => GetRed(ref _bang, 2); + public override SyntaxToken Name => GetRed(ref _name, 3); + public override MarkupMiscAttributeContentSyntax MiscAttributeContent => GetRed(ref _miscAttributeContent, 4); + public override SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); + public override ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupEndTagSyntax)Green).ChunkGenerator; internal override SyntaxNode GetNodeSlot(int index) => index switch @@ -934,15 +1015,22 @@ public MarkupEndTagSyntax Update(SyntaxToken openAngle, SyntaxToken forwardSlash return this; } - public MarkupEndTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupEndTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, forwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupEndTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, ForwardSlash, bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupEndTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, ForwardSlash, Bang, name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupEndTagSyntax WithMiscAttributeContent(MarkupMiscAttributeContentSyntax miscAttributeContent) => Update(OpenAngle, ForwardSlash, Bang, Name, miscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupEndTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, closeAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithOpenAngleCore(SyntaxToken openAngle) => WithOpenAngle(openAngle); + public new MarkupEndTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithForwardSlashCore(SyntaxToken forwardSlash) => WithForwardSlash(forwardSlash); + public new MarkupEndTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, forwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithBangCore(SyntaxToken bang) => WithBang(bang); + public new MarkupEndTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, ForwardSlash, bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithNameCore(SyntaxToken name) => WithName(name); + public new MarkupEndTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, ForwardSlash, Bang, name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithMiscAttributeContentCore(MarkupMiscAttributeContentSyntax miscAttributeContent) => WithMiscAttributeContent(miscAttributeContent); + public new MarkupEndTagSyntax WithMiscAttributeContent(MarkupMiscAttributeContentSyntax miscAttributeContent) => Update(OpenAngle, ForwardSlash, Bang, Name, miscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithCloseAngleCore(SyntaxToken closeAngle) => WithCloseAngle(closeAngle); + public new MarkupEndTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, closeAngle, ChunkGenerator); public MarkupEndTagSyntax WithChunkGenerator(ISpanChunkGenerator chunkGenerator) => Update(OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, chunkGenerator); + internal override BaseMarkupEndTagSyntax AddMiscAttributeContentChildrenCore(params RazorSyntaxNode[] items) => AddMiscAttributeContentChildren(items); - public MarkupEndTagSyntax AddMiscAttributeContentChildren(params RazorSyntaxNode[] items) + public new MarkupEndTagSyntax AddMiscAttributeContentChildren(params RazorSyntaxNode[] items) { var _miscAttributeContent = this.MiscAttributeContent ?? SyntaxFactory.MarkupMiscAttributeContent(); return this.WithMiscAttributeContent(_miscAttributeContent.WithChildren(_miscAttributeContent.Children.AddRange(items))); @@ -1009,7 +1097,7 @@ public MarkupTagHelperElementSyntax Update(MarkupTagHelperStartTagSyntax startTa public MarkupTagHelperElementSyntax AddBody(params RazorSyntaxNode[] items) => WithBody(this.Body.AddRange(items)); } -internal sealed partial class MarkupTagHelperStartTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupTagHelperStartTagSyntax : BaseMarkupStartTagSyntax { private SyntaxToken _openAngle; private SyntaxToken _bang; @@ -1023,13 +1111,13 @@ internal MarkupTagHelperStartTagSyntax(GreenNode green, SyntaxNode parent, int p { } - public SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); - public SyntaxToken Bang => GetRed(ref _bang, 1); - public SyntaxToken Name => GetRed(ref _name, 2); - public SyntaxList Attributes => new SyntaxList(GetRed(ref _attributes, 3)); - public SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 4); - public SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); - public ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupTagHelperStartTagSyntax)Green).ChunkGenerator; + public override SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); + public override SyntaxToken Bang => GetRed(ref _bang, 1); + public override SyntaxToken Name => GetRed(ref _name, 2); + public override SyntaxList Attributes => new SyntaxList(GetRed(ref _attributes, 3)); + public override SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 4); + public override SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); + public override ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupTagHelperStartTagSyntax)Green).ChunkGenerator; internal override SyntaxNode GetNodeSlot(int index) => index switch @@ -1073,18 +1161,25 @@ public MarkupTagHelperStartTagSyntax Update(SyntaxToken openAngle, SyntaxToken b return this; } - public MarkupTagHelperStartTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, Bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupTagHelperStartTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupTagHelperStartTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, Bang, name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupTagHelperStartTagSyntax WithAttributes(SyntaxList attributes) => Update(OpenAngle, Bang, Name, attributes, ForwardSlash, CloseAngle, ChunkGenerator); - public MarkupTagHelperStartTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, Bang, Name, Attributes, forwardSlash, CloseAngle, ChunkGenerator); - public MarkupTagHelperStartTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, Bang, Name, Attributes, ForwardSlash, closeAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithOpenAngleCore(SyntaxToken openAngle) => WithOpenAngle(openAngle); + public new MarkupTagHelperStartTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, Bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithBangCore(SyntaxToken bang) => WithBang(bang); + public new MarkupTagHelperStartTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, bang, Name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithNameCore(SyntaxToken name) => WithName(name); + public new MarkupTagHelperStartTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, Bang, name, Attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithAttributesCore(SyntaxList attributes) => WithAttributes(attributes); + public new MarkupTagHelperStartTagSyntax WithAttributes(SyntaxList attributes) => Update(OpenAngle, Bang, Name, attributes, ForwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithForwardSlashCore(SyntaxToken forwardSlash) => WithForwardSlash(forwardSlash); + public new MarkupTagHelperStartTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, Bang, Name, Attributes, forwardSlash, CloseAngle, ChunkGenerator); + internal override BaseMarkupStartTagSyntax WithCloseAngleCore(SyntaxToken closeAngle) => WithCloseAngle(closeAngle); + public new MarkupTagHelperStartTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, Bang, Name, Attributes, ForwardSlash, closeAngle, ChunkGenerator); public MarkupTagHelperStartTagSyntax WithChunkGenerator(ISpanChunkGenerator chunkGenerator) => Update(OpenAngle, Bang, Name, Attributes, ForwardSlash, CloseAngle, chunkGenerator); + internal override BaseMarkupStartTagSyntax AddAttributesCore(params RazorSyntaxNode[] items) => AddAttributes(items); - public MarkupTagHelperStartTagSyntax AddAttributes(params RazorSyntaxNode[] items) => WithAttributes(this.Attributes.AddRange(items)); + public new MarkupTagHelperStartTagSyntax AddAttributes(params RazorSyntaxNode[] items) => WithAttributes(this.Attributes.AddRange(items)); } -internal sealed partial class MarkupTagHelperEndTagSyntax : MarkupSyntaxNode +internal sealed partial class MarkupTagHelperEndTagSyntax : BaseMarkupEndTagSyntax { private SyntaxToken _openAngle; private SyntaxToken _forwardSlash; @@ -1098,13 +1193,13 @@ internal MarkupTagHelperEndTagSyntax(GreenNode green, SyntaxNode parent, int pos { } - public SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); - public SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 1); - public SyntaxToken Bang => GetRed(ref _bang, 2); - public SyntaxToken Name => GetRed(ref _name, 3); - public MarkupMiscAttributeContentSyntax MiscAttributeContent => GetRed(ref _miscAttributeContent, 4); - public SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); - public ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupTagHelperEndTagSyntax)Green).ChunkGenerator; + public override SyntaxToken OpenAngle => GetRedAtZero(ref _openAngle); + public override SyntaxToken ForwardSlash => GetRed(ref _forwardSlash, 1); + public override SyntaxToken Bang => GetRed(ref _bang, 2); + public override SyntaxToken Name => GetRed(ref _name, 3); + public override MarkupMiscAttributeContentSyntax MiscAttributeContent => GetRed(ref _miscAttributeContent, 4); + public override SyntaxToken CloseAngle => GetRed(ref _closeAngle, 5); + public override ISpanChunkGenerator ChunkGenerator => ((InternalSyntax.MarkupTagHelperEndTagSyntax)Green).ChunkGenerator; internal override SyntaxNode GetNodeSlot(int index) => index switch @@ -1148,15 +1243,22 @@ public MarkupTagHelperEndTagSyntax Update(SyntaxToken openAngle, SyntaxToken for return this; } - public MarkupTagHelperEndTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupTagHelperEndTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, forwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupTagHelperEndTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, ForwardSlash, bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupTagHelperEndTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, ForwardSlash, Bang, name, MiscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupTagHelperEndTagSyntax WithMiscAttributeContent(MarkupMiscAttributeContentSyntax miscAttributeContent) => Update(OpenAngle, ForwardSlash, Bang, Name, miscAttributeContent, CloseAngle, ChunkGenerator); - public MarkupTagHelperEndTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, closeAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithOpenAngleCore(SyntaxToken openAngle) => WithOpenAngle(openAngle); + public new MarkupTagHelperEndTagSyntax WithOpenAngle(SyntaxToken openAngle) => Update(openAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithForwardSlashCore(SyntaxToken forwardSlash) => WithForwardSlash(forwardSlash); + public new MarkupTagHelperEndTagSyntax WithForwardSlash(SyntaxToken forwardSlash) => Update(OpenAngle, forwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithBangCore(SyntaxToken bang) => WithBang(bang); + public new MarkupTagHelperEndTagSyntax WithBang(SyntaxToken bang) => Update(OpenAngle, ForwardSlash, bang, Name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithNameCore(SyntaxToken name) => WithName(name); + public new MarkupTagHelperEndTagSyntax WithName(SyntaxToken name) => Update(OpenAngle, ForwardSlash, Bang, name, MiscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithMiscAttributeContentCore(MarkupMiscAttributeContentSyntax miscAttributeContent) => WithMiscAttributeContent(miscAttributeContent); + public new MarkupTagHelperEndTagSyntax WithMiscAttributeContent(MarkupMiscAttributeContentSyntax miscAttributeContent) => Update(OpenAngle, ForwardSlash, Bang, Name, miscAttributeContent, CloseAngle, ChunkGenerator); + internal override BaseMarkupEndTagSyntax WithCloseAngleCore(SyntaxToken closeAngle) => WithCloseAngle(closeAngle); + public new MarkupTagHelperEndTagSyntax WithCloseAngle(SyntaxToken closeAngle) => Update(OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, closeAngle, ChunkGenerator); public MarkupTagHelperEndTagSyntax WithChunkGenerator(ISpanChunkGenerator chunkGenerator) => Update(OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle, chunkGenerator); + internal override BaseMarkupEndTagSyntax AddMiscAttributeContentChildrenCore(params RazorSyntaxNode[] items) => AddMiscAttributeContentChildren(items); - public MarkupTagHelperEndTagSyntax AddMiscAttributeContentChildren(params RazorSyntaxNode[] items) + public new MarkupTagHelperEndTagSyntax AddMiscAttributeContentChildren(params RazorSyntaxNode[] items) { var _miscAttributeContent = this.MiscAttributeContent ?? SyntaxFactory.MarkupMiscAttributeContent(); return this.WithMiscAttributeContent(_miscAttributeContent.WithChildren(_miscAttributeContent.Children.AddRange(items))); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/GreenNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/GreenNode.cs index 17494e4305d..fd0a86964e1 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/GreenNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/GreenNode.cs @@ -259,10 +259,9 @@ public void WriteTo(TextWriter writer) continue; } - var firstIndex = GetFirstNonNullChildIndex(node); - var lastIndex = GetLastNonNullChildIndex(node); + var slotCount = node.SlotCount; - for (var i = lastIndex; i >= firstIndex; i--) + for (var i = slotCount - 1; i >= 0; i--) { if (node.GetSlot(i) is GreenNode child) { @@ -270,38 +269,6 @@ public void WriteTo(TextWriter writer) } } } - - static int GetFirstNonNullChildIndex(GreenNode node) - { - var slotCount = node.SlotCount; - var firstIndex = 0; - - for (; firstIndex < slotCount; firstIndex++) - { - if (node.GetSlot(firstIndex) is not null) - { - break; - } - } - - return firstIndex; - } - - static int GetLastNonNullChildIndex(GreenNode node) - { - var slotCount = node.SlotCount; - var lastIndex = slotCount - 1; - - for (; lastIndex >= 0; lastIndex--) - { - if (node.GetSlot(lastIndex) is not null) - { - break; - } - } - - return lastIndex; - } } protected virtual void WriteTokenTo(TextWriter writer) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/IStartTagSyntaxNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/IStartTagSyntaxNode.cs deleted file mode 100644 index 9e091b2debd..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/IStartTagSyntaxNode.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal interface IStartTagSyntaxNode -{ - SyntaxList Attributes { get; } - SyntaxToken Name { get; } - TextSpan Span { get; } - int SpanStart { get; } - SyntaxNode? Parent { get; } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupEndTagSyntax.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupEndTagSyntax.cs index 00fb5144564..c7d20003715 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupEndTagSyntax.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupEndTagSyntax.cs @@ -5,26 +5,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax; internal partial class MarkupEndTagSyntax { - private SyntaxNode _lazyChildren; - public bool IsMarkupTransition => ((InternalSyntax.MarkupEndTagSyntax)Green).IsMarkupTransition; - public SyntaxList LegacyChildren - { - get - { - var children = _lazyChildren ?? InterlockedOperations.Initialize(ref _lazyChildren, GetLegacyChildren()); - - return new SyntaxList(children); - - SyntaxNode GetLegacyChildren() - { - return SyntaxUtilities.GetEndTagLegacyChildren(this, OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle); - } - } - } - public string GetTagNameWithOptionalBang() { return Name.IsMissing ? string.Empty : Bang?.Content + Name.Content; diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupStartTagSyntax.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupStartTagSyntax.cs index 621f95cf331..5f97196f811 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupStartTagSyntax.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupStartTagSyntax.cs @@ -5,28 +5,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax; -internal partial class MarkupStartTagSyntax : IStartTagSyntaxNode +internal partial class MarkupStartTagSyntax { - private SyntaxNode _lazyChildren; - public bool IsMarkupTransition => ((InternalSyntax.MarkupStartTagSyntax)Green).IsMarkupTransition; - public SyntaxList LegacyChildren - { - get - { - var children = _lazyChildren ?? InterlockedOperations.Initialize(ref _lazyChildren, GetLegacyChildren()); - - return new SyntaxList(children); - - SyntaxNode GetLegacyChildren() - { - return SyntaxUtilities.GetStartTagLegacyChildren(this, Attributes, OpenAngle, Bang, Name, ForwardSlash, CloseAngle); - } - } - } - public string GetTagNameWithOptionalBang() { return Name.IsMissing ? string.Empty : Bang?.Content + Name.Content; diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupTagHelperEndTagSyntax.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupTagHelperEndTagSyntax.cs deleted file mode 100644 index 2ad4fcea9f6..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupTagHelperEndTagSyntax.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal partial class MarkupTagHelperEndTagSyntax -{ - private SyntaxNode _lazyChildren; - - // Copied directly from MarkupEndTagSyntax Children & GetLegacyChildren. - - public SyntaxList LegacyChildren - { - get - { - var children = _lazyChildren ?? InterlockedOperations.Initialize(ref _lazyChildren, GetLegacyChildren()); - - return new SyntaxList(children); - - SyntaxNode GetLegacyChildren() - { - return SyntaxUtilities.GetEndTagLegacyChildren(this, OpenAngle, ForwardSlash, Bang, Name, MiscAttributeContent, CloseAngle); - } - } - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupTagHelperStartTagSyntax.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupTagHelperStartTagSyntax.cs deleted file mode 100644 index de3fb1a7b74..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/MarkupTagHelperStartTagSyntax.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal partial class MarkupTagHelperStartTagSyntax : IStartTagSyntaxNode -{ - private SyntaxNode _lazyChildren; - - public SyntaxList LegacyChildren - { - get - { - var children = _lazyChildren ?? InterlockedOperations.Initialize(ref _lazyChildren, GetLegacyChildren()); - - return new SyntaxList(children); - - SyntaxNode GetLegacyChildren() - { - return SyntaxUtilities.GetStartTagLegacyChildren(this, Attributes, OpenAngle, Bang, Name, ForwardSlash, CloseAngle); - } - } - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Syntax.xml b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Syntax.xml index c8118619ea1..8150a5b46d0 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Syntax.xml +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/Syntax.xml @@ -1,4 +1,4 @@ - + @@ -104,14 +104,7 @@ - - - - - - - - + @@ -129,9 +122,8 @@ - - - + + @@ -149,6 +141,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -156,45 +194,45 @@ - + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxBuilderExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxBuilderExtensions.cs new file mode 100644 index 00000000000..7484822eb1e --- /dev/null +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxBuilderExtensions.cs @@ -0,0 +1,306 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Razor.PooledObjects; + +namespace Microsoft.AspNetCore.Razor.Language.Syntax; + +internal static class SyntaxBuilderExtensions +{ + /// + /// Produces a for a from the + /// contents of . + /// + public static GreenNode? ToGreenListNode(ref readonly this PooledArrayBuilder builder) + where TNode : SyntaxNode + { + switch (builder.Count) + { + case 0: + return null; + case 1: + return builder[0].Green; + case 2: + return InternalSyntax.SyntaxList.List(builder[0].Green, builder[1].Green); + case 3: + return InternalSyntax.SyntaxList.List(builder[0].Green, builder[1].Green, builder[2].Green); + + case int count: + var copy = new ArrayElement[count]; + + for (var i = 0; i < count; i++) + { + copy[i].Value = builder[i].Green; + } + + return InternalSyntax.SyntaxList.List(copy); + } + } + + /// + /// Produces a for a from the + /// contents of and clears it. + /// + /// + /// + /// + /// Because this method mutates , builder.AsRef() must be + /// called if it is is declared in a using statement. + /// + /// + /// + /// using PooledArrayBuilder<GreenNode> builder = []; + /// + /// // Use AsRef() inline + /// builder.AsRef().ToGreenListNodeAndClear(); + /// + /// // Declare a local ref variable with AsRef() to avoid taking a ref multiple times. + /// ref PooledArrayBuilder<GreenNode> builderRef = ref builder.AsRef(); + /// builderRef.ToGreenListNodeAndClear(); + /// + /// + public static GreenNode? ToGreenListNodeAndClear(ref this PooledArrayBuilder builder) + where TNode : SyntaxNode + { + var result = builder.ToGreenListNode(); + builder.Clear(); + + return result; + } + + /// + /// Produces a for a from the + /// contents of . + /// + public static SyntaxNode? ToListNode(ref readonly this PooledArrayBuilder builder) + where TNode : SyntaxNode + => builder.ToListNode(parent: null, position: 0); + + /// + /// Produces a with the given + /// for a from the contents of . + /// + public static SyntaxNode? ToListNode( + ref readonly this PooledArrayBuilder builder, SyntaxNode parent) + where TNode : SyntaxNode + => builder.ToGreenListNode() is GreenNode listNode + ? listNode.CreateRed(parent, parent.Position) + : null; + + /// + /// Produces a with the given + /// and for a from the contents + /// of . + /// + public static SyntaxNode? ToListNode( + ref readonly this PooledArrayBuilder builder, SyntaxNode? parent, int position) + where TNode : SyntaxNode + => builder.ToGreenListNode() is GreenNode listNode + ? listNode.CreateRed(parent, position) + : null; + + /// + /// Produces a for a from the + /// contents of and clears it. + /// + /// + /// + /// + /// Because this method mutates , builder.AsRef() must be + /// called if it is is declared in a using statement. + /// + /// + /// + /// using PooledArrayBuilder<SyntaxNode> builder = []; + /// + /// // Use AsRef() inline + /// builder.AsRef().ToListNodeAndClear(); + /// + /// // Declare a local ref variable with AsRef() to avoid taking a ref multiple times. + /// ref PooledArrayBuilder<SyntaxNode> builderRef = ref builder.AsRef(); + /// builderRef.ToListNodeAndClear(); + /// + /// + public static SyntaxNode? ToListNodeAndClear(ref this PooledArrayBuilder builder) + where TNode : SyntaxNode + { + return builder.ToListNodeAndClear(parent: null, position: 0); + } + + /// + /// Produces a with the given + /// for a from the contents of + /// and clears it. + /// + /// + /// + /// + /// Because this method mutates , builder.AsRef() must be + /// called if it is is declared in a using statement. + /// + /// + /// + /// using PooledArrayBuilder<SyntaxNode> builder = []; + /// + /// // Use AsRef() inline + /// builder.AsRef().ToListNodeAndClear(parent); + /// + /// // Declare a local ref variable with AsRef() to avoid taking a ref multiple times. + /// ref PooledArrayBuilder<SyntaxNode> builderRef = ref builder.AsRef(); + /// builderRef.ToListNodeAndClear(parent); + /// + /// + public static SyntaxNode? ToListNodeAndClear( + ref this PooledArrayBuilder builder, SyntaxNode parent) + where TNode : SyntaxNode + { + var result = builder.ToListNode(parent, parent.Position); + builder.Clear(); + + return result; + } + + /// + /// Produces a with the given + /// and for a from the contents + /// of and clears it. + /// + /// + /// + /// + /// Because this method mutates , builder.AsRef() must be + /// called if it is is declared in a using statement. + /// + /// + /// + /// using PooledArrayBuilder<SyntaxNode> builder = []; + /// + /// // Use AsRef() inline + /// builder.AsRef().ToListNodeAndClear(parent, position); + /// + /// // Declare a local ref variable with AsRef() to avoid taking a ref multiple times. + /// ref PooledArrayBuilder<SyntaxNode> builderRef = ref builder.AsRef(); + /// builderRef.ToListNodeAndClear(parent, position); + /// + /// + public static SyntaxNode? ToListNodeAndClear( + ref this PooledArrayBuilder builder, SyntaxNode? parent, int position) + where TNode : SyntaxNode + { + var result = builder.ToListNode(parent, position); + builder.Clear(); + + return result; + } + + /// + /// Produces a from the contents of . + /// + public static SyntaxList ToList(ref readonly this PooledArrayBuilder builder) + where TNode : SyntaxNode + => builder.ToList(parent: null, position: 0); + + /// + /// Produces a with the given + /// from the contents of . + /// + public static SyntaxList ToList( + ref readonly this PooledArrayBuilder builder, SyntaxNode parent) + where TNode : SyntaxNode + => builder.ToList(parent, parent.Position); + + /// + /// Produces a with the given + /// and from the contents of . + /// + public static SyntaxList ToList( + ref readonly this PooledArrayBuilder builder, SyntaxNode? parent, int position) + where TNode : SyntaxNode + => builder.ToGreenListNode() is GreenNode listNode + ? new(listNode.CreateRed(parent, position)) + : default; + + /// + /// Produces a from the contents of and clears it. + /// + /// + /// + /// + /// Because this method mutates , builder.AsRef() must be + /// called if it is is declared in a using statement. + /// + /// + /// + /// using PooledArrayBuilder<SyntaxNode> builder = []; + /// + /// // Use AsRef() inline + /// builder.AsRef().ToListAndClear(); + /// + /// // Declare a local ref variable with AsRef() to avoid taking a ref multiple times. + /// ref PooledArrayBuilder<SyntaxNode> builderRef = ref builder.AsRef(); + /// builderRef.ToListAndClear(); + /// + /// + public static SyntaxList ToListAndClear(ref this PooledArrayBuilder builder) + where TNode : SyntaxNode + => builder.ToListAndClear(parent: null, position: 0); + + /// + /// Produces a with the given + /// from the contents of and clears it. + /// + /// + /// + /// + /// Because this method mutates , builder.AsRef() must be + /// called if it is is declared in a using statement. + /// + /// + /// + /// using PooledArrayBuilder<SyntaxNode> builder = []; + /// + /// // Use AsRef() inline + /// builder.AsRef().ToListAndClear(); + /// + /// // Declare a local ref variable with AsRef() to avoid taking a ref multiple times. + /// ref PooledArrayBuilder<SyntaxNode> builderRef = ref builder.AsRef(); + /// builderRef.ToListAndClear(); + /// + /// + public static SyntaxList ToListAndClear( + ref this PooledArrayBuilder builder, SyntaxNode parent) + where TNode : SyntaxNode + => builder.ToListAndClear(parent, parent.Position); + + /// + /// Produces a with the given + /// and from the contents of and clears it. + /// + /// + /// + /// + /// Because this method mutates , builder.AsRef() must be + /// called if it is is declared in a using statement. + /// + /// + /// + /// using PooledArrayBuilder<SyntaxNode> builder = []; + /// + /// // Use AsRef() inline + /// builder.AsRef().ToListAndClear(parent, position); + /// + /// // Declare a local ref variable with AsRef() to avoid taking a ref multiple times. + /// ref PooledArrayBuilder<SyntaxNode> builderRef = ref builder.AsRef(); + /// builderRef.ToListAndClear(parent, position); + /// + /// + public static SyntaxList ToListAndClear( + ref this PooledArrayBuilder builder, SyntaxNode? parent, int position) + where TNode : SyntaxNode + { + var result = builder.ToList(parent, position); + builder.Clear(); + + return result; + } +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxFactory.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxFactory.cs index e872725fb04..57c57d4d552 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxFactory.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxFactory.cs @@ -3,6 +3,8 @@ #nullable disable +using Microsoft.AspNetCore.Razor.Language.Legacy; + namespace Microsoft.AspNetCore.Razor.Language.Syntax; internal static partial class SyntaxFactory @@ -21,4 +23,12 @@ internal static SyntaxToken MissingToken(SyntaxKind kind, params RazorDiagnostic { return new SyntaxToken(InternalSyntax.SyntaxFactory.MissingToken(kind, diagnostics), parent: null, position: 0); } + +#nullable enable + + public static MarkupTextLiteralSyntax MarkupTextLiteral(SyntaxToken token, ISpanChunkGenerator? chunkGenerator) + => MarkupTextLiteral(new SyntaxList(token), chunkGenerator); + + public static RazorMetaCodeSyntax RazorMetaCode(SyntaxToken token, ISpanChunkGenerator? chunkGenerator) + => RazorMetaCode(new SyntaxList(token), chunkGenerator); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilder.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilder.cs deleted file mode 100644 index 41419a5c4ba..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilder.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using Microsoft.AspNetCore.Razor.PooledObjects; - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal class SyntaxListBuilder(int initialCapacity) -{ - private ArrayElement[] _nodes = new ArrayElement[initialCapacity]; - - public int Capacity => _nodes.Length; - - public void SetCapacityIfLarger(int newCapacity) - { - if (newCapacity > _nodes.Length) - { - Array.Resize(ref _nodes, newCapacity); - } - } - - public int Count { get; private set; } - - public void Clear() - { - Array.Clear(_nodes, 0, Count); - Count = 0; - } - - internal void ClearInternal() - { - if (_nodes.Length > DefaultPool.MaximumObjectSize) - { - Array.Resize(ref _nodes, DefaultPool.MaximumObjectSize); - Count = DefaultPool.MaximumObjectSize; - } - - Clear(); - } - - public void Add(SyntaxNode item) - { - AddInternal(item.Green); - } - - internal void AddInternal(GreenNode item) - { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - if (Count >= _nodes.Length) - { - Grow(Count == 0 ? 8 : _nodes.Length * 2); - } - - _nodes[Count++].Value = item; - } - - public void AddRange(SyntaxNode[] items) - { - AddRange(items, 0, items.Length); - } - - public void AddRange(SyntaxNode[] items, int offset, int length) - { - if (Count + length > _nodes.Length) - { - Grow(Count + length); - } - - for (int i = offset, j = Count; i < offset + length; ++i, ++j) - { - _nodes[j].Value = items[i].Green; - } - - var start = Count; - Count += length; - Validate(start, Count); - } - - [Conditional("DEBUG")] - private void Validate(int start, int end) - { - for (var i = start; i < end; i++) - { - if (_nodes[i].Value == null) - { - throw new ArgumentException("Cannot add a null node."); - } - } - } - - public void AddRange(SyntaxList list) - { - AddRange(list, 0, list.Count); - } - - public void AddRange(SyntaxList list, int offset, int count) - { - if (Count + count > _nodes.Length) - { - Grow(Count + count); - } - - var dst = Count; - for (int i = offset, limit = offset + count; i < limit; i++) - { - _nodes[dst].Value = list.ItemInternal(i)!.Green; - dst++; - } - - var start = Count; - Count += count; - Validate(start, Count); - } - - public void AddRange(SyntaxList list) where TNode : SyntaxNode - { - AddRange(list, 0, list.Count); - } - - public void AddRange(SyntaxList list, int offset, int count) where TNode : SyntaxNode - { - AddRange(new SyntaxList(list.Node), offset, count); - } - - private void Grow(int newSize) - { - Array.Resize(ref _nodes, newSize); - } - - public bool Any(SyntaxKind kind) - { - for (var i = 0; i < Count; i++) - { - if (_nodes[i].Value.Kind == kind) - { - return true; - } - } - - return false; - } - - internal GreenNode? ToListNode() - { - switch (Count) - { - case 0: - return null; - case 1: - return _nodes[0].Value; - case 2: - return InternalSyntax.SyntaxList.List(_nodes[0].Value, _nodes[1].Value); - case 3: - return InternalSyntax.SyntaxList.List(_nodes[0].Value, _nodes[1].Value, _nodes[2].Value); - default: - var tmp = new ArrayElement[Count]; - for (var i = 0; i < Count; i++) - { - tmp[i].Value = _nodes[i].Value; - } - - return InternalSyntax.SyntaxList.List(tmp); - } - } - - public static implicit operator SyntaxList(SyntaxListBuilder builder) - { - if (builder == null) - { - return default; - } - - return builder.ToList(); - } - - internal void RemoveLast() - { - Count -= 1; - _nodes[Count] = default; - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderExtensions.cs deleted file mode 100644 index da0c4c681be..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal static class SyntaxListBuilderExtensions -{ - public static SyntaxList ToList(this SyntaxListBuilder builder) - { - if (builder == null || builder.Count == 0) - { - return default; - } - - return new SyntaxList(builder.ToListNode().AssumeNotNull().CreateRed()); - } - - public static SyntaxList ToList(this SyntaxListBuilder builder, SyntaxNode parent) - { - if (builder == null || builder.Count == 0) - { - return default; - } - - return new SyntaxList(builder.ToListNode().AssumeNotNull().CreateRed(parent, parent.Position)); - } - - public static SyntaxList ToList(this SyntaxListBuilder builder) - where TNode : SyntaxNode - { - if (builder == null || builder.Count == 0) - { - return default; - } - - return new SyntaxList(builder.ToListNode().AssumeNotNull().CreateRed()); - } - - public static SyntaxList ToList(this SyntaxListBuilder builder, SyntaxNode parent) - where TNode : SyntaxNode - { - if (builder == null || builder.Count == 0) - { - return default; - } - - return new SyntaxList(builder.ToListNode().AssumeNotNull().CreateRed(parent, parent.Position)); - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderOfT.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderOfT.cs deleted file mode 100644 index e504970c2de..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderOfT.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal readonly struct SyntaxListBuilder - where TNode : SyntaxNode -{ - internal readonly SyntaxListBuilder Builder; - - internal SyntaxListBuilder(SyntaxListBuilder builder) - { - Builder = builder; - } - - public bool IsNull => Builder == null; - - public int Capacity => Builder.Capacity; - - public void SetCapacityIfLarger(int newCapacity) - => Builder.SetCapacityIfLarger(newCapacity); - - public int Count => Builder.Count; - - public void Clear() - { - Builder.Clear(); - } - - internal void ClearInternal() - { - Builder.ClearInternal(); - } - - public SyntaxListBuilder Add(TNode node) - { - Builder.Add(node); - return this; - } - - public void AddRange(TNode[] items, int offset, int length) - { - Builder.AddRange(items, offset, length); - } - - public void AddRange(SyntaxList nodes) - { - Builder.AddRange(nodes); - } - - public void AddRange(SyntaxList nodes, int offset, int length) - { - Builder.AddRange(nodes, offset, length); - } - - public bool Any(SyntaxKind kind) - { - return Builder.Any(kind); - } - - public SyntaxList ToList() - { - return Builder.ToList(); - } - - public SyntaxList ToList(SyntaxNode parent) - { - return Builder.ToList(parent); - } - - public SyntaxList Consume() - { - var list = ToList(); - Clear(); - return list; - } - - public static implicit operator SyntaxListBuilder(SyntaxListBuilder builder) - { - return builder.Builder; - } - - public static implicit operator SyntaxList(SyntaxListBuilder builder) - { - if (builder.Builder != null) - { - return builder.ToList(); - } - - return default; - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.Policy.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.Policy.cs deleted file mode 100644 index c96e81fa0b0..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.Policy.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Extensions.ObjectPool; - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal static partial class SyntaxListBuilderPool -{ - private sealed class Policy : IPooledObjectPolicy - { - private const int InitializeBuilderSize = 8; - - public static readonly Policy Instance = new(); - - private Policy() - { - } - - public SyntaxListBuilder Create() => new(InitializeBuilderSize); - - public bool Return(SyntaxListBuilder builder) - { - builder.ClearInternal(); - return true; - } - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.PooledBuilder.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.PooledBuilder.cs deleted file mode 100644 index 1db8c0d55f6..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.PooledBuilder.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Extensions.ObjectPool; - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal static partial class SyntaxListBuilderPool -{ - public struct PooledBuilder : IDisposable - where T : SyntaxNode - { - private readonly ObjectPool _pool; - private SyntaxListBuilder? _builder; - - public readonly SyntaxListBuilder Builder => _builder.GetValueOrDefault(); - - public PooledBuilder(ObjectPool pool) - : this() - { - _pool = pool; - _builder = new SyntaxListBuilder(pool.Get()); - } - - public void Dispose() - { - if (_builder is { } obj) - { - _pool.Return(obj.Builder); - _builder = null; - } - } - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.cs deleted file mode 100644 index b5af52bdc07..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListBuilderPool.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Razor.PooledObjects; -using Microsoft.Extensions.ObjectPool; - -namespace Microsoft.AspNetCore.Razor.Language.Syntax; - -internal static partial class SyntaxListBuilderPool -{ - public static readonly ObjectPool Default = DefaultPool.Create(Policy.Instance); - - public static PooledObject GetPooledBuilder() - => Default.GetPooledBuilder(); - - public static PooledObject GetPooledBuilder(out SyntaxListBuilder builder) - => Default.GetPooledBuilder(out builder); - - public static PooledBuilder GetPooledBuilder() - where T : SyntaxNode - => Default.GetPooledBuilder(); - - public static PooledBuilder GetPooledBuilder(out SyntaxListBuilder builder) - where T : SyntaxNode - => Default.GetPooledBuilder(out builder); - - public static PooledObject GetPooledBuilder(this ObjectPool pool) - => new(pool); - - public static PooledObject GetPooledBuilder(this ObjectPool pool, out SyntaxListBuilder builder) - { - var pooledObject = pool.GetPooledBuilder(); - builder = pooledObject.Object; - return pooledObject; - } - - public static PooledBuilder GetPooledBuilder(this ObjectPool pool) - where T : SyntaxNode - => new(pool); - - public static PooledBuilder GetPooledBuilder(this ObjectPool pool, out SyntaxListBuilder builder) - where T : SyntaxNode - { - var pooledBuilder = pool.GetPooledBuilder(); - builder = pooledBuilder.Builder; - return pooledBuilder; - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListOfT.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListOfT.cs index 550e4327bf7..146255586eb 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListOfT.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxListOfT.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using Microsoft.CodeAnalysis.Text; +using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language.Syntax; @@ -35,13 +36,8 @@ public SyntaxList(SyntaxList nodes) private static SyntaxNode? CreateNode(SyntaxList nodes) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); - builder.SetCapacityIfLarger(nodes.Count); - - foreach (var node in nodes) - { - builder.Add(node); - } + using var builder = new PooledArrayBuilder(nodes.Count); + builder.AddRange(nodes); return builder.ToList().Node; } @@ -299,8 +295,7 @@ public bool Any() public SyntaxList Where(Func predicate) { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); - builder.SetCapacityIfLarger(Count); + using var builder = new PooledArrayBuilder(Count); foreach (var node in this) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxNode.cs index 377df89cda3..7fbcf0b4581 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxNode.cs @@ -6,7 +6,9 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Linq; using System.Threading; +using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.AspNetCore.Razor.Language.Syntax; @@ -163,29 +165,32 @@ internal virtual int GetChildPosition(int index) internal SyntaxList GetTokens() { - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var tokens); + using PooledArrayBuilder tokens = []; + using PooledArrayBuilder stack = []; - AddTokens(this, tokens); + stack.Push(this); - return tokens; - - static void AddTokens(SyntaxNode current, SyntaxListBuilder tokens) + while (stack.Count > 0) { + var current = stack.Pop(); + if (current.SlotCount == 0 && current is SyntaxToken token) { - // Token tokens.Add(token); - return; } - - for (var i = 0; i < current.SlotCount; i++) + else { - if (current.GetNodeSlot(i) is { } child) + for (var i = current.SlotCount - 1; i >= 0; i--) { - AddTokens(child, tokens); + if (current.GetNodeSlot(i) is { } child) + { + stack.Push(child); + } } } } + + return tokens.ToList(); } /// @@ -270,6 +275,11 @@ public IEnumerable DescendantNodesAndSelf(Func? de return DescendantNodesImpl(Span, descendIntoChildren, includeSelf: true); } + public IEnumerable DescendantTokens(Func? descendIntoChildren = null) + { + return DescendantNodesImpl(Span, descendIntoChildren, includeSelf: true).OfType(); + } + protected internal SyntaxNode ReplaceCore( IEnumerable? nodes = null, Func? computeReplacementNode = null) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxRewriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxRewriter.cs index 0f863eb1e08..9ad1a77e468 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxRewriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxRewriter.cs @@ -3,6 +3,8 @@ #nullable disable +using Microsoft.AspNetCore.Razor.PooledObjects; + namespace Microsoft.AspNetCore.Razor.Language.Syntax; internal abstract partial class SyntaxRewriter : SyntaxVisitor @@ -27,31 +29,45 @@ public override SyntaxNode Visit(SyntaxNode node) } } - public virtual SyntaxList VisitList(SyntaxList list) where TNode : SyntaxNode + public virtual SyntaxList VisitList(SyntaxList list) + where TNode : SyntaxNode { - SyntaxListBuilder alternate = null; - for (int i = 0, n = list.Count; i < n; i++) + var count = list.Count; + if (count == 0) + { + return list; + } + + using PooledArrayBuilder builder = []; + + var isUpdating = false; + + for (var i = 0; i < count; i++) { var item = list[i]; + var visited = VisitListElement(item); - if (item != visited && alternate == null) + + if (item != visited && !isUpdating) { - alternate = new SyntaxListBuilder(n); - alternate.AddRange(list, 0, i); + // The list is being updated, so we need to initialize the builder + // add the items we've seen so far. + builder.SetCapacityIfLarger(count); + + builder.AddRange(list, index: 0, count: i); + + isUpdating = true; } - if (alternate != null && visited != null) + if (isUpdating && visited != null) { - alternate.Add(visited); + builder.Add(visited); } } - if (alternate != null) - { - return alternate.ToList(); - } - - return list; + return isUpdating + ? builder.ToList() + : list; } public override SyntaxNode VisitToken(SyntaxToken token) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxUtilities.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxUtilities.cs index 8d2ccb8f065..aa3ad1b0073 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxUtilities.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Syntax/SyntaxUtilities.cs @@ -1,189 +1,49 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Linq; +using System; using Microsoft.AspNetCore.Razor.Language.Legacy; +using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language.Syntax; internal static class SyntaxUtilities { - public static MarkupTextLiteralSyntax MergeTextLiterals(params MarkupTextLiteralSyntax[] literalSyntaxes) + public static MarkupTextLiteralSyntax MergeTextLiterals(params ReadOnlySpan literals) { SyntaxNode? parent = null; var position = 0; var seenFirstLiteral = false; - var builder = InternalSyntax.SyntaxListBuilder.Create(); - foreach (var syntax in literalSyntaxes) + using PooledArrayBuilder builder = []; + + foreach (var literal in literals) { - if (syntax == null) + if (literal == null) { continue; } - else if (!seenFirstLiteral) + + if (!seenFirstLiteral) { // Set the parent and position of the merged literal to the value of the first non-null literal. - parent = syntax.Parent; - position = syntax.Position; + parent = literal.Parent; + position = literal.Position; seenFirstLiteral = true; } - foreach (var token in syntax.LiteralTokens) - { - builder.Add(token.Green); - } - } - - var mergedLiteralSyntax = InternalSyntax.SyntaxFactory.MarkupTextLiteral( - builder.ToList(), chunkGenerator: null); - - return (MarkupTextLiteralSyntax)mergedLiteralSyntax.CreateRed(parent, position); - } - - internal static SyntaxNode GetStartTagLegacyChildren( - SyntaxNode @this, - SyntaxList attributes, - SyntaxToken openAngle, - SyntaxToken bang, - SyntaxToken name, - SyntaxToken forwardSlash, - SyntaxToken closeAngle) - { - // This method returns the children of this start tag in legacy format. - // This is needed to generate the same classified spans as the legacy syntax tree. - using var _1 = SyntaxListBuilderPool.GetPooledBuilder(out var builder); - using var _2 = SyntaxListBuilderPool.GetPooledBuilder(out var tokens); - - SpanEditHandler? acceptsAnyHandler = null; - var containsAttributesContent = false; - var editHandler = @this.GetEditHandler(); - var chunkGenerator = @this.GetChunkGenerator(); - if (editHandler != null) - { - // We want to know if this tag contains non-whitespace attribute content to set the appropriate AcceptedCharacters. - // The prefix of a start tag(E.g '|') will have 'Any' accepted characters if non-whitespace attribute content exists. - acceptsAnyHandler = SpanEditHandler.CreateDefault(AcceptedCharactersInternal.Any); - foreach (var attribute in attributes) - { - if (!string.IsNullOrWhiteSpace(attribute.GetContent())) - { - containsAttributesContent = true; - break; - } - } - } - - if (!openAngle.IsMissing) - { - tokens.Add(openAngle); - } - - if (bang != null) - { - builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume(), chunkGenerator).WithEditHandler(acceptsAnyHandler)); - - tokens.Add(bang); - var acceptsNoneHandler = editHandler == null ? null : SpanEditHandler.CreateDefault(AcceptedCharactersInternal.None); - builder.Add(SyntaxFactory.RazorMetaCode(tokens.Consume(), chunkGenerator).WithEditHandler(acceptsNoneHandler)); + builder.AddRange(literal.LiteralTokens); } - if (!name.IsMissing) - { - tokens.Add(name); - } - - builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume(), chunkGenerator).WithEditHandler(containsAttributesContent ? acceptsAnyHandler : editHandler)); - - builder.AddRange(attributes); - - if (forwardSlash != null) - { - tokens.Add(forwardSlash); - } - - if (!closeAngle.IsMissing) - { - tokens.Add(closeAngle); - } - - if (tokens.Count > 0) - { - builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume(), chunkGenerator).WithEditHandler(editHandler)); - } - - return builder.ToListNode().AssumeNotNull().CreateRed(@this, @this.Position); + return (MarkupTextLiteralSyntax)InternalSyntax.SyntaxFactory + .MarkupTextLiteral( + literalTokens: builder.ToGreenListNode().ToGreenList(), + chunkGenerator: null) + .CreateRed(parent, position); } - internal static SyntaxNode GetEndTagLegacyChildren( - SyntaxNode @this, - SyntaxToken openAngle, - SyntaxToken forwardSlash, - SyntaxToken bang, - SyntaxToken name, - MarkupMiscAttributeContentSyntax miscAttributeContent, - SyntaxToken closeAngle) - { - // This method returns the children of this end tag in legacy format. - // This is needed to generate the same classified spans as the legacy syntax tree. - using var _1 = SyntaxListBuilderPool.GetPooledBuilder(out var builder); - using var _2 = SyntaxListBuilderPool.GetPooledBuilder(out var tokens); - - var editHandler = @this.GetEditHandler(); - var chunkGenerator = @this.GetChunkGenerator(); - - if (!openAngle.IsMissing) - { - tokens.Add(openAngle); - } - - if (!forwardSlash.IsMissing) - { - tokens.Add(forwardSlash); - } - - if (bang != null) - { - SpanEditHandler? acceptsAnyHandler = null; - SpanEditHandler? acceptsNoneHandler = null; - if (editHandler != null) - { - acceptsAnyHandler = SpanEditHandler.CreateDefault(AcceptedCharactersInternal.Any); - acceptsNoneHandler = SpanEditHandler.CreateDefault(AcceptedCharactersInternal.None); - } - - // The prefix of an end tag(E.g '|') will have 'Any' accepted characters if a bang exists. - builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume(), chunkGenerator).WithEditHandler(acceptsAnyHandler)); - - tokens.Add(bang); - builder.Add(SyntaxFactory.RazorMetaCode(tokens.Consume(), chunkGenerator).WithEditHandler(acceptsNoneHandler)); - } - - if (!name.IsMissing) - { - tokens.Add(name); - } - - if (miscAttributeContent?.Children != null && miscAttributeContent.Children.Count > 0) - { - foreach (var content in miscAttributeContent.Children) - { - tokens.AddRange(((MarkupTextLiteralSyntax)content).LiteralTokens); - } - } - - if (!closeAngle.IsMissing) - { - tokens.Add(closeAngle); - } - - builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume(), chunkGenerator).WithEditHandler(editHandler)); - - return builder.ToListNode().AssumeNotNull().CreateRed(@this, @this.Position); - } - - internal static SyntaxList GetRewrittenMarkupStartTagChildren(MarkupStartTagSyntax node, bool includeEditHandler = false) + internal static SyntaxList GetRewrittenMarkupStartTagChildren( + MarkupStartTagSyntax node, bool includeEditHandler = false) { // Rewrites the children of the start tag to look like the legacy syntax tree. if (node.IsMarkupTransition) @@ -191,73 +51,77 @@ internal static SyntaxList GetRewrittenMarkupStartTagChildren(M return GetRewrittenMarkupNodeChildren(node, node.ChunkGenerator, includeEditHandler); } + using PooledArrayBuilder newChildren = []; + using PooledArrayBuilder literals = []; + SpanEditHandler? latestEditHandler = null; - var children = node.LegacyChildren; - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var newChildren); - var literals = new List(); - foreach (var child in children) + foreach (var child in node.LegacyChildren) { - if (child is MarkupTextLiteralSyntax literal) + switch (child) { - literals.Add(literal); + case MarkupTextLiteralSyntax literal: + literals.Add(literal); - if (includeEditHandler) - { - latestEditHandler = literal.GetEditHandler() ?? latestEditHandler; - } - } - else if (child is MarkupMiscAttributeContentSyntax miscContent) - { - foreach (var contentChild in miscContent.Children) - { - if (contentChild is MarkupTextLiteralSyntax contentLiteral) + if (includeEditHandler) + { + latestEditHandler = literal.GetEditHandler() ?? latestEditHandler; + } + + break; + + case MarkupMiscAttributeContentSyntax miscContent: + foreach (var contentChild in miscContent.Children) { - literals.Add(contentLiteral); + if (contentChild is MarkupTextLiteralSyntax contentLiteral) + { + literals.Add(contentLiteral); - if (includeEditHandler) + if (includeEditHandler) + { + latestEditHandler = contentLiteral.GetEditHandler() ?? latestEditHandler; + } + } + else { - latestEditHandler = contentLiteral.GetEditHandler() ?? latestEditHandler; + // Pop stack + AddLiteralIsIfNeeded(); + newChildren.Add(contentChild); } } - else - { - // Pop stack - AddLiteralIfExists(); - newChildren.Add(contentChild); - } - } - } - else - { - AddLiteralIfExists(); - newChildren.Add(child); + + break; + + default: + AddLiteralIsIfNeeded(); + newChildren.Add(child); + break; } } - AddLiteralIfExists(); + AddLiteralIsIfNeeded(); return newChildren.ToList(node); - void AddLiteralIfExists() + void AddLiteralIsIfNeeded() { if (literals.Count > 0) { - var mergedLiteral = MergeTextLiterals(literals.ToArray()); + var mergedLiteral = MergeTextLiterals(literals.ToArrayAndClear()); if (includeEditHandler) { mergedLiteral = mergedLiteral.WithEditHandler(latestEditHandler); } - literals.Clear(); latestEditHandler = null; newChildren.Add(mergedLiteral); } } } - internal static SyntaxList GetRewrittenMarkupEndTagChildren(MarkupEndTagSyntax node, bool includeEditHandler = false) + internal static SyntaxList GetRewrittenMarkupEndTagChildren( + MarkupEndTagSyntax node, bool includeEditHandler = false) { // Rewrites the children of the end tag to look like the legacy syntax tree. return node.IsMarkupTransition @@ -266,18 +130,22 @@ internal static SyntaxList GetRewrittenMarkupEndTagChildren(Mar } internal static SyntaxList GetRewrittenMarkupNodeChildren( - MarkupSyntaxNode node, - ISpanChunkGenerator chunkGenerator, - bool includeEditHandler = false) + MarkupSyntaxNode node, ISpanChunkGenerator chunkGenerator, bool includeEditHandler = false) { - var tokens = node.DescendantNodes().OfType().Where(t => !t.IsMissing).ToArray(); + using PooledArrayBuilder builder = []; - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); - builder.AddRange(tokens, 0, tokens.Length); - var transitionTokens = builder.ToList(); + foreach (var descendantNode in node.DescendantNodes()) + { + if (descendantNode is SyntaxToken { IsMissing: false } token) + { + builder.Add(token); + } + } - var markupTransition = SyntaxFactory - .MarkupTransition(transitionTokens, chunkGenerator).Green + var markupTransition = InternalSyntax.SyntaxFactory + .MarkupTransition( + builder.ToGreenListNode().ToGreenList(), + chunkGenerator) .CreateRed(node, node.Position); if (includeEditHandler && node.GetEditHandler() is { } editHandler) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeActions/Razor/ComponentAccessibilityCodeActionProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeActions/Razor/ComponentAccessibilityCodeActionProvider.cs index 9e9fef93798..b187e022300 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeActions/Razor/ComponentAccessibilityCodeActionProvider.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeActions/Razor/ComponentAccessibilityCodeActionProvider.cs @@ -22,13 +22,12 @@ namespace Microsoft.CodeAnalysis.Razor.CodeActions; -using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode; - internal class ComponentAccessibilityCodeActionProvider(IFileSystem fileSystem) : IRazorCodeActionProvider { private readonly IFileSystem _fileSystem = fileSystem; - public async Task> ProvideAsync(RazorCodeActionContext context, CancellationToken cancellationToken) + public async Task> ProvideAsync( + RazorCodeActionContext context, CancellationToken cancellationToken) { // Locate cursor var node = context.CodeDocument.GetSyntaxTree().Root.FindInnermostNode(context.StartAbsoluteIndex); @@ -42,7 +41,7 @@ public async Task> ProvideAsync(RazorC // We also check for tag helper start tags here, because an invalid start tag with a valid tag helper // anywhere in it would otherwise not match. We rely on the IsTagUnknown method below, to ensure we // only offer on actual potential component tags (because it checks for the compiler diagnostic) - var startTag = (IStartTagSyntaxNode?)node.FirstAncestorOrSelf(n => n is IStartTagSyntaxNode); + var startTag = node.FirstAncestorOrSelf(); if (startTag is null) { return []; @@ -79,7 +78,7 @@ public async Task> ProvideAsync(RazorC return [.. codeActions]; } - private static bool IsApplicableTag(IStartTagSyntaxNode startTag) + private static bool IsApplicableTag(BaseMarkupStartTagSyntax startTag) { if (startTag.Name.Width == 0) { @@ -90,7 +89,8 @@ private static bool IsApplicableTag(IStartTagSyntaxNode startTag) return true; } - private void AddCreateComponentFromTag(RazorCodeActionContext context, IStartTagSyntaxNode startTag, List container) + private void AddCreateComponentFromTag( + RazorCodeActionContext context, BaseMarkupStartTagSyntax startTag, List container) { if (!context.SupportsFileCreation) { @@ -127,7 +127,11 @@ private void AddCreateComponentFromTag(RazorCodeActionContext context, IStartTag container.Add(codeAction); } - private static async Task AddComponentAccessFromTagAsync(RazorCodeActionContext context, IStartTagSyntaxNode startTag, List container, CancellationToken cancellationToken) + private static async Task AddComponentAccessFromTagAsync( + RazorCodeActionContext context, + BaseMarkupStartTagSyntax startTag, + List container, + CancellationToken cancellationToken) { var haveAddedNonQualifiedFix = false; @@ -196,7 +200,8 @@ private static async Task AddComponentAccessFromTagAsync(RazorCodeActionContext } } - private static async Task> FindMatchingTagHelpersAsync(RazorCodeActionContext context, IStartTagSyntaxNode startTag, CancellationToken cancellationToken) + private static async Task> FindMatchingTagHelpersAsync( + RazorCodeActionContext context, BaseMarkupStartTagSyntax startTag, CancellationToken cancellationToken) { // Get all data necessary for matching var tagName = startTag.Name.Content; @@ -240,7 +245,12 @@ private static async Task> FindMatchingTagHelpersA return [.. matching.Values]; } - private static bool SatisfiesRules(ImmutableArray tagMatchingRules, ReadOnlySpan tagNameWithoutPrefix, ReadOnlySpan parentTagNameWithoutPrefix, ImmutableArray> tagAttributes, out bool caseInsensitiveMatch) + private static bool SatisfiesRules( + ImmutableArray tagMatchingRules, + ReadOnlySpan tagNameWithoutPrefix, + ReadOnlySpan parentTagNameWithoutPrefix, + ImmutableArray> tagAttributes, + out bool caseInsensitiveMatch) { caseInsensitiveMatch = false; @@ -277,7 +287,8 @@ private static bool SatisfiesRules(ImmutableArray tag return true; } - private static WorkspaceEdit CreateRenameTagEdit(RazorCodeActionContext context, IStartTagSyntaxNode startTag, string newTagName) + private static WorkspaceEdit CreateRenameTagEdit( + RazorCodeActionContext context, BaseMarkupStartTagSyntax startTag, string newTagName) { using var textEdits = new PooledArrayBuilder>(); var codeDocumentIdentifier = new OptionalVersionedTextDocumentIdentifier() { Uri = context.Request.TextDocument.Uri }; @@ -306,7 +317,7 @@ private static WorkspaceEdit CreateRenameTagEdit(RazorCodeActionContext context, }; } - private static bool IsTagUnknown(IStartTagSyntaxNode startTag, RazorCodeActionContext context) + private static bool IsTagUnknown(BaseMarkupStartTagSyntax startTag, RazorCodeActionContext context) { foreach (var diagnostic in context.CodeDocument.GetCSharpDocument().Diagnostics) { diff --git a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/RazorSyntaxTreePartialParser.cs b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/RazorSyntaxTreePartialParser.cs index d741a810a22..92d584244fd 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/RazorSyntaxTreePartialParser.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/RazorSyntaxTreePartialParser.cs @@ -56,7 +56,7 @@ private PartialParseResultInternal GetPartialParseResult(SourceChange change) // Try the last change owner if (_lastChangeOwner is not null) { - var editHandler = _lastChangeOwner.GetEditHandler() ?? SpanEditHandler.CreateDefault(AcceptedCharactersInternal.Any); + var editHandler = _lastChangeOwner.GetEditHandler() ?? SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any); if (editHandler.OwnsChange(_lastChangeOwner, change)) { var editResult = editHandler.ApplyChange(_lastChangeOwner, change); @@ -82,7 +82,7 @@ private PartialParseResultInternal GetPartialParseResult(SourceChange change) } else if (_lastChangeOwner is not null) { - var editHandler = _lastChangeOwner.GetEditHandler() ?? SpanEditHandler.CreateDefault(AcceptedCharactersInternal.Any); + var editHandler = _lastChangeOwner.GetEditHandler() ?? SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any); var editResult = editHandler.ApplyChange(_lastChangeOwner, change); result = editResult.Result; if ((editResult.Result & PartialParseResultInternal.Rejected) != PartialParseResultInternal.Rejected) diff --git a/src/Razor/test/Microsoft.VisualStudio.LegacyEditor.Razor.Test/Indentation/RazorIndentationFactsTest.cs b/src/Razor/test/Microsoft.VisualStudio.LegacyEditor.Razor.Test/Indentation/RazorIndentationFactsTest.cs index f5ebb4fb4cc..2f579fcb727 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LegacyEditor.Razor.Test/Indentation/RazorIndentationFactsTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LegacyEditor.Razor.Test/Indentation/RazorIndentationFactsTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Syntax; +using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Test.Common.Editor; using Microsoft.AspNetCore.Razor.Test.Common.VisualStudio; @@ -37,7 +38,7 @@ public void GetPreviousLineEndIndex_ReturnsPreviousLine() public void IsCSharpOpenCurlyBrace_SpanWithLeftBrace_ReturnTrue() { // Arrange - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; builder.Add(SyntaxFactory.Token(SyntaxKind.LeftBrace, "{")); var child = SyntaxFactory.RazorMetaCode(builder.ToList(), chunkGenerator: null); @@ -57,7 +58,7 @@ public void IsCSharpOpenCurlyBrace_SpanWithUnsupportedSymbolType_ReturnFalse(str { // Arrange var symbolType = (SyntaxKind)symbolTypeObject; - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; builder.Add(SyntaxFactory.Token(symbolType, content)); var child = SyntaxFactory.MarkupTextLiteral(builder.ToList(), chunkGenerator: null); @@ -72,7 +73,7 @@ public void IsCSharpOpenCurlyBrace_SpanWithUnsupportedSymbolType_ReturnFalse(str public void IsCSharpOpenCurlyBrace_MultipleSymbols_ReturnFalse() { // Arrange - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; builder.Add(SyntaxFactory.Token(SyntaxKind.Identifier, "hello")); builder.Add(SyntaxFactory.Token(SyntaxKind.Comma, ",")); var child = SyntaxFactory.MarkupTextLiteral(builder.ToList(), chunkGenerator: null); @@ -88,7 +89,7 @@ public void IsCSharpOpenCurlyBrace_MultipleSymbols_ReturnFalse() public void IsCSharpOpenCurlyBrace_SpanWithHtmlSymbol_ReturnFalse() { // Arrange - using var _ = SyntaxListBuilderPool.GetPooledBuilder(out var builder); + using PooledArrayBuilder builder = []; builder.Add(SyntaxFactory.Token(SyntaxKind.Text, "hello")); var child = SyntaxFactory.MarkupTextLiteral(builder.ToList(), chunkGenerator: null); diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArrayBuilder`1.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArrayBuilder`1.cs index a38dc22722e..fbdb82bed9f 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArrayBuilder`1.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/PooledObjects/PooledArrayBuilder`1.cs @@ -233,6 +233,26 @@ private void SetInlineElement(int index, T value) public readonly int Count => _builder?.Count ?? _inlineCount; + public readonly int Capacity + => _builder?.Capacity ?? _capacity ?? InlineCapacity; + + public void SetCapacityIfLarger(int value) + { + if (value > Capacity) + { + if (TryGetBuilder(out var builder)) + { + Debug.Assert(value > builder.Capacity); + builder.Capacity = value; + } + else + { + Debug.Assert(value > (_capacity ?? InlineCapacity)); + _capacity = value; + } + } + } + public void Add(T item) { if (TryGetBuilderAndEnsureCapacity(out var builder)) @@ -289,6 +309,49 @@ public void AddRange(ReadOnlySpan items) } } + public void AddRange(TList list) + where TList : struct, IReadOnlyList + { + AddRange(list, 0, list.Count); + } + + public void AddRange(TList list, int index, int count) + where TList : struct, IReadOnlyList + { + if (count == 0) + { + return; + } + + if (TryGetBuilderAndEnsureCapacity(out var builder)) + { + for (var i = index; i < count; i++) + { + builder.Add(list[i]); + } + + return; + } + + if (_inlineCount + count <= InlineCapacity) + { + for (var i = index; i < count; i++) + { + SetInlineElement(_inlineCount, list[i]); + _inlineCount++; + } + } + else + { + MoveInlineItemsToBuilder(); + + for (var i = index; i < count; i++) + { + _builder.Add(list[i]); + } + } + } + public void AddRange(IEnumerable items) { if (TryGetBuilderAndEnsureCapacity(out var builder)) @@ -476,6 +539,14 @@ public readonly T[] ToArray() }; } + public T[] ToArrayAndClear() + { + var result = ToArray(); + Clear(); + + return result; + } + public void Push(T item) { Add(item);