Skip to content

Conversation

@DustinCampbell
Copy link
Member

@DustinCampbell DustinCampbell commented Jun 20, 2025

This represents several changes to improve efficiency and reduce allocations in the ClassifiedSpanVisitor, TagHelperSpanVisitor, and FormattingVisitor.

  • Pool ClassifiedSpanVisitor instances to reduce allocations.
  • Move legacy code to the Legacy namespace, isolating it from current implementations.
  • Avoid creating delegates in ClassifiedSpanVisitor and FormattingVisitor.
  • Add SpanComputer helper to avoid creating syntax nodes just to visit them in ClassifiedSpanVisitor and FormattingVisitor.
  • Use pooled array builder for formatting spans to reduce memory pressure.
  • Add streamlined (i.e. non-Legacy) versions of GetTagHelpers and GetClassifiedSpans in tooling. These only produce the data needed to support tooling's GetLanguageKind.
  • Other small tweaks

CI Build: https://dev.azure.com/dnceng/internal/_build/results?buildId=2734526&view=results
Test Insertion: https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/645479
Toolset Run: https://dev.azure.com/dnceng/internal/_build/results?buildId=2734527&view=results

Provides additional overloads for the `SyntaxToken`
factory methods in `SyntaxFactory`.

These new overloads allow specifying the parent syntax node,
position, and index, offering greater flexibility when creating
syntax tokens.

In addition, use one of the new SyntaxFactory.Token overloads in ClassifiedSpanVisitor rather than constructor a SyntaxToken directly.
Refactors ClassifiedSpanVisitor to use a `BlockSaver` ref struct
to manage block scopes, improving readability and reducing
code duplication. Removes unnecessary base visit action delegates.
Refactors span writing logic in the `ClassifiedSpanVisitor` to improve
readability and eliminate redundant null checks.

Retrieves `AcceptedCharacters` directly within the method, using the
node's edit handler or defaulting to `Any`.
Also adds an assertion to validate current block during span writing.
Changes span creation to only compute the current block's
span when it is first needed. This avoids redundant
computations, improving performance.
Uses a `SourceSpanComputer` to efficiently create markup spans for
attributes, avoiding unnecessary string allocations and improving
performance of the `ClassifiedSpanVisitor`.
Moves the `ClassifiedSpanVisitor` to the `Legacy` namespace since it is only accessed by code in that namespace.

This change is part of an effort to better organize the Razor compiler's internal structure. It specifically isolates legacy code.
Replaces the pooled `ImmutableArray.Builder` with a pooled `ClassifiedSpanVisitor` to avoid allocating a new visitor instance each time. The `ImmutableArray.Builder` is still effectively pooled, since it is owned by `ClassifiedSpanVisitor`. This change reduces overall memory allocations and improves performance.
Replaces explicit null checks with `ArgHelper.ThrowIfNull` in
`RazorSyntaxTreeExtensions` for conciseness.
Moves the `TagHelperSpanVisitor` to the `Legacy` namespace since it is only accessed by code in that namespace.

This change is part of an effort to better organize the Razor compiler's internal structure. It specifically isolates legacy code.
Refactors formatting visitor to use a ref struct SpanComputer
for generating text spans, improving efficiency.

Consolidates span creation logic and simplifies block management
with a BlockSaver struct for cleaner code.
This method is no longer used outside of SyntaxUtilities.
Improves performance by utilizing a pooled array builder
to avoid allocations when collecting formatting spans.

This change reduces memory pressure and improves the
efficiency of the formatting process.
Utilizes a pooled array builder when computing tag helper spans, reducing memory allocations.

Avoids calling into the legacy `GetTagHelperSpans` method, further improving performance by avoiding allocations of `TagHelperSpanVisitor`.

In addition, returns `SourceSpans` rather than `TagHelperSpanInternal` since tooling doesn't use the tag helper binding info.
Refactors the way classified spans are computed for Razor code documents intooling.

This change replaces the usage of legacy compiler APIs with a new `ClassifiedSpanVisitor`.
The new implementation is streamlined to only produce the information needed for the `GetLanguageKind()` API, improving performance and reducing dependencies.

This also includes changing the `ClassifiedSpanInternal` to `ClassifiedSpan` and removing the dependency on `Microsoft.AspNetCore.Razor.Language.Legacy`.
@DustinCampbell DustinCampbell requested review from a team as code owners June 20, 2025 22:02
@DustinCampbell DustinCampbell requested a review from ToddGrun June 20, 2025 22:02
Copy link
Member

@davidwengier davidwengier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@ToddGrun ToddGrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@DustinCampbell
Copy link
Member Author

Pingubg @dotnet/razor-compiler for a second review.

@RikkiGibson RikkiGibson modified the milestones: Next, 18.0 P1 Aug 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants