Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
51add26
Introduce SyntaxBuilderExtensions
DustinCampbell May 7, 2025
e6a3b1a
SyntaxUtilities: Refactor and use PooledArrayBuilder<T>
DustinCampbell May 7, 2025
20d00d2
Introduce BaseMarkupStartTagSyntax and BaseMarkupEndTagSyntax
DustinCampbell May 7, 2025
a4b379e
Move legacy children and support methods to base markup tag types
DustinCampbell May 7, 2025
b321fc7
Share default SpanEditHandlers with no tokenizer
DustinCampbell May 7, 2025
af4e555
DefaultRazorIntermediateNodeLoweringPass: Prefer PooledArrayBuilder<T>
DustinCampbell May 7, 2025
f4421fc
TagHelperBlockRewriter: Prefer PooledArrayBuilder<T>
DustinCampbell May 7, 2025
64c22d2
TagHelperParserTreeRewriter: Prefer PooledArrayBuilder<T>
DustinCampbell May 7, 2025
7072fe1
SyntaxRewriter: Prefer PooledArrayBuilder<T> over SyntaxListBuilder
DustinCampbell May 8, 2025
79c7889
SyntaxNode.GetTokens(): Prefer PooledArrayBuilder<T>
DustinCampbell May 8, 2025
8b5d33f
SyntaxList<T>: Prefer PooledArrayBuilder<T>
DustinCampbell May 8, 2025
6c02180
Prefer PooledArrayBuilder<T> in unit tests
DustinCampbell May 8, 2025
0961eae
Remove SyntaxListBuilderPool
DustinCampbell May 8, 2025
4647eb6
Remove SyntaxListBuilder, SyntaxListBuilder<T>, and extension methods
DustinCampbell May 8, 2025
bd25c02
DefaultRazorIntermediateNodeLoweringPhase: small amount of clean up
DustinCampbell May 8, 2025
5584ad7
Remove unnecessary extra loops in GreenNode.WriteTo
DustinCampbell May 8, 2025
db96bbc
BaseMethodStartTagSyntax: Don't allocate for each attribute's content
DustinCampbell May 8, 2025
562477a
Merge branch 'main' into list-builders
DustinCampbell May 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SyntaxToken>(out var builder);
using PooledArrayBuilder<SyntaxToken> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -281,7 +282,7 @@ public void ContainsInvalidContent_ValidContent_ReturnsFalse(string content)

private static SyntaxNode GetSpan(SourceLocation start, string content)
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var builder);
using PooledArrayBuilder<SyntaxToken> builder = [];

var tokens = NativeCSharpLanguageCharacteristics.Instance.TokenizeString(content).ToArray();
foreach (var token in tokens)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -492,38 +492,30 @@ public override void VisitCSharpStatementLiteral(CSharpStatementLiteralSyntax no
return node.GetSourceSpan(SourceDocument);
}

protected static SyntaxList<SyntaxToken> MergeLiterals(
SyntaxList<SyntaxToken>? literal1,
SyntaxList<SyntaxToken>? literal2,
SyntaxList<SyntaxToken>? literal3 = null,
SyntaxList<SyntaxToken>? literal4 = null,
SyntaxList<SyntaxToken>? literal5 = null)
protected static SyntaxList<SyntaxToken> MergeTokenLists(params ReadOnlySpan<SyntaxList<SyntaxToken>?> tokenLists)
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(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<SyntaxToken>(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();
Expand Down Expand Up @@ -574,7 +566,10 @@ public bool TryCast<TNode>(out ImmutableArray<TNode> 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);

Expand Down Expand Up @@ -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<SyntaxToken>(node.EqualsToken) : default,
new SyntaxList<SyntaxToken>(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();
Expand All @@ -629,7 +625,7 @@ public override void VisitMarkupAttributeBlock(MarkupAttributeBlockSyntax node)

if (children.TryCast<MarkupLiteralAttributeValueSyntax>(out var attributeLiteralArray))
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var builder);
using var builder = new PooledArrayBuilder<SyntaxToken>();

foreach (var literal in attributeLiteralArray)
{
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1188,7 +1189,7 @@ private void VisitAttributeValue(SyntaxNode node)

if (children.TryCast<MarkupLiteralAttributeValueSyntax>(out var attributeLiteralArray))
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var builder);
using PooledArrayBuilder<SyntaxToken> builder = [];

foreach (var literal in attributeLiteralArray)
{
Expand All @@ -1201,7 +1202,7 @@ private void VisitAttributeValue(SyntaxNode node)
}
else if (children.TryCast<MarkupTextLiteralSyntax>(out var markupLiteralArray))
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var builder);
using PooledArrayBuilder<SyntaxToken> builder = [];

foreach (var literal in markupLiteralArray)
{
Expand All @@ -1213,7 +1214,7 @@ private void VisitAttributeValue(SyntaxNode node)
}
else if (children.TryCast<CSharpExpressionLiteralSyntax>(out var expressionLiteralArray))
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var builder);
using PooledArrayBuilder<SyntaxToken> builder = [];

SpanEditHandler editHandler = null;
ISpanChunkGenerator generator = null;
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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())
{
Expand Down Expand Up @@ -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<SyntaxToken>() : new SyntaxList<SyntaxToken>(node.EqualsToken),
new SyntaxList<SyntaxToken>(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();
Expand All @@ -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();
Expand Down Expand Up @@ -2158,7 +2163,7 @@ private void VisitAttributeValue(SyntaxNode node)

if (children.TryCast<MarkupLiteralAttributeValueSyntax>(out var attributeLiteralArray))
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var valueTokens);
using PooledArrayBuilder<SyntaxToken> valueTokens = [];

foreach (var literal in attributeLiteralArray)
{
Expand All @@ -2171,7 +2176,7 @@ private void VisitAttributeValue(SyntaxNode node)
}
else if (children.TryCast<MarkupTextLiteralSyntax>(out var markupLiteralArray))
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var builder);
using PooledArrayBuilder<SyntaxToken> builder = [];

foreach (var literal in markupLiteralArray)
{
Expand All @@ -2183,7 +2188,7 @@ private void VisitAttributeValue(SyntaxNode node)
}
else if (children.TryCast<CSharpExpressionLiteralSyntax>(out var expressionLiteralArray))
{
using var _ = SyntaxListBuilderPool.GetPooledBuilder<SyntaxToken>(out var builder);
using PooledArrayBuilder<SyntaxToken> builder = [];

ISpanChunkGenerator generator = null;
SpanEditHandler editHandler = null;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public static TNode WithEditHandler<TNode>(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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, IEnumerable<Syntax.InternalSyntax.SyntaxToken>> NoTokenizer = _ => [];

private static readonly ImmutableArray<SpanEditHandler> 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<string, IEnumerable<Syntax.InternalSyntax.SyntaxToken>> Tokenizer { get; init; }

public static SpanEditHandler CreateDefault(AcceptedCharactersInternal acceptedCharacters)
public static SpanEditHandler GetDefault(AcceptedCharactersInternal acceptedCharacters)
{
return CreateDefault(static c => Enumerable.Empty<Syntax.InternalSyntax.SyntaxToken>(), 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<string, IEnumerable<Syntax.InternalSyntax.SyntaxToken>> tokenizer, AcceptedCharactersInternal acceptedCharacters)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<string, IEnumerable<SyntaxToken>> DefaultTokenizer = static content => Enumerable.Empty<SyntaxToken>();
private static readonly SpanEditHandler DefaultEditHandler = SpanEditHandler.CreateDefault(AcceptedCharactersInternal.Any);
private static readonly Func<string, IEnumerable<SyntaxToken>> DefaultTokenizer = SpanEditHandler.NoTokenizer;
private static readonly SpanEditHandler DefaultEditHandler = SpanEditHandler.GetDefault(AcceptedCharactersInternal.Any);

private readonly Func<string, IEnumerable<SyntaxToken>>? _defaultLanguageTokenizer;
private readonly SpanEditHandler? _defaultLanguageEditHandler;
Expand Down
Loading
Loading