Skip to content

Port Wrap with Tag endpoint to cohosting#11994

Closed
Copilot wants to merge 7 commits intomainfrom
copilot/fix-11993
Closed

Port Wrap with Tag endpoint to cohosting#11994
Copilot wants to merge 7 commits intomainfrom
copilot/fix-11993

Conversation

Copy link
Contributor

Copilot AI commented Jun 30, 2025

This PR ports the existing WrapWithTagEndpoint to the cohosting architecture, providing better performance and consistency with other Razor language services.

Changes Made

1. Remote Service Infrastructure

  • IRemoteWrapWithTagService - New interface in Microsoft.CodeAnalysis.Razor.Workspaces

    • IsValidWrapWithTagLocationAsync - Validates if position supports wrap with tag operation
    • FixHtmlTextEditsAsync - Fixes text edits containing tilde characters from generated HTML
  • RemoteWrapWithTagService - Implementation in Microsoft.CodeAnalysis.Remote.Razor

    • Copied exact validation logic from original WrapWithTagEndpoint
    • Handles whitespace offset adjustments, language kind detection, and implicit expressions
    • Implements text edit fixing using FormattingUtilities in the remote context

2. Cohost Endpoint

  • CohostWrapWithTagEndpoint - New endpoint in Microsoft.VisualStudio.LanguageServices.Razor
    • Validates position via remote service call
    • Delegates to HTML server when position is valid
    • Fixes text edits through remote service
    • Follows established cohosting patterns with proper MEF registration

3. Service Registration

  • Added Microsoft.VisualStudio.Razor.WrapWithTag entry to Services.props
  • Added IRemoteWrapWithTagService to RazorServices.MessagePackServices

4. Comprehensive Tests

  • Valid HTML locations return HTML server response
  • C# locations properly return null (not supported)
  • Implicit expressions are correctly supported
  • Tilde character fixing is properly handled

Architecture Benefits

  • Performance: Remote service execution reduces devenv memory usage
  • Consistency: Follows established cohosting patterns used by other Razor services
  • Maintainability: Single source of validation logic shared between language server and cohost
  • Compatibility: Maintains exact behavior of original endpoint

The implementation preserves all existing functionality while moving computation to the out-of-process service for better performance in Visual Studio.

Fixes #11993.

Warning

Firewall rules blocked me from connecting to one or more addresses

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 80zvsblobprodcus35.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/razor/razor/.dotnet/dotnet msbuild /m /nologo /clp:Summary /v:minimal /nr:true /warnaserror /p:TreatWarningsAsErrors=true /p:ContinuousIntegrationBuild=false /home/REDACTED/work/razor/razor/artifacts/toolset/restore.proj /t:__WriteToolsetLocation /clp:ErrorsOnly;NoSummary /p:__ToolsetLocationOutputFile=/home/REDACTED/work/razor/razor/artifacts/toolset/9.0.0-beta.25255.5.txt (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits June 30, 2025 11:06
Co-authored-by: davidwengier <754264+davidwengier@users.noreply.github.com>
Co-authored-by: davidwengier <754264+davidwengier@users.noreply.github.com>

protected override bool RequiresLSPSolution => true;

public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
Copy link
Member

@davidwengier davidwengier Jun 30, 2025

Choose a reason for hiding this comment

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

We don't need to register anything for WrapWithTag, so this method is not needed, and the class doesn't need to implement IDynamicRegistrationProvider, or have the export attribute for that interface

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 252a33b. Removed the IDynamicRegistrationProvider implementation, export attribute, and GetRegistrations method since WrapWithTag doesn't need registration.

Copilot AI and others added 2 commits June 30, 2025 11:11
Co-authored-by: davidwengier <754264+davidwengier@users.noreply.github.com>
Co-authored-by: davidwengier <754264+davidwengier@users.noreply.github.com>
Copilot AI changed the title [WIP] Port Wrap with Tag endpoint to cohosting Port Wrap with Tag endpoint to cohosting Jun 30, 2025
Copilot AI requested a review from davidwengier June 30, 2025 11:13
Removed unnecessary dynamic registration implementation as WrapWithTag doesn't need registration:
- Removed IDynamicRegistrationProvider interface implementation
- Removed Export attribute for IDynamicRegistrationProvider
- Removed GetRegistrations method

Addresses feedback from @davidwengier

Co-authored-by: davidwengier <754264+davidwengier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jun 30, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 80zvsblobprodcus35.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/razor/razor/.dotnet/dotnet msbuild /m /nologo /clp:Summary /v:minimal /nr:true /warnaserror /p:TreatWarningsAsErrors=true /p:ContinuousIntegrationBuild=false /home/REDACTED/work/razor/razor/artifacts/toolset/restore.proj /t:__WriteToolsetLocation /clp:ErrorsOnly;NoSummary /p:__ToolsetLocationOutputFile=/home/REDACTED/work/razor/razor/artifacts/toolset/9.0.0-beta.25255.5.txt (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


internal interface IRemoteWrapWithTagService
{
ValueTask<RemoteResponse<bool>> IsValidWrapWithTagLocationAsync(RazorPinnedSolutionInfoWrapper solutionInfo, DocumentId razorDocumentId, LspRange range, CancellationToken cancellationToken);
Copy link
Member

Choose a reason for hiding this comment

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

The range parameter here is of type LspRange, which won't work as the types need to be serializable by MessagePack. Making it a LinePositionSpan will work, and there is a ToLinePositionSpan() method that can be called in the endpoint to do the conversion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in fdbcebd. Changed the range parameter from LspRange to LinePositionSpan for MessagePack serialization compatibility. The cohost endpoint now converts using ToLinePositionSpan() before calling the remote service.

var codeDocument = await context.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
var sourceText = codeDocument.Source.Text;

if (range?.Start is not { } start ||
Copy link
Member

Choose a reason for hiding this comment

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

Rather than copy the code from WrapWithTagEndpoint, lets instead share it. Can do this by creating a new class in the Workspaces project, which has a static method that contains this logic. Call it WrapWithTagHelper, and it can have a method like IsValidWrappingRange(RazorCodeDocument codeDocument, LinePositionSpan range).

Also update the existing WrapWithTagEndpoint to call this new shared function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in fdbcebd. Created WrapWithTagHelper in the Workspaces project with shared validation logic. Updated both the original WrapWithTagEndpoint and RemoteWrapWithTagService to use this shared helper, eliminating code duplication while preserving exact behavior.


private async ValueTask<Response> IsValidWrapWithTagLocationAsync(
RemoteDocumentContext context,
LspRange range,
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be LinePositionSpan too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in fdbcebd. Updated the parameter type to LinePositionSpan to match the interface changes for MessagePack serialization.

…tionSpan

Co-authored-by: davidwengier <754264+davidwengier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jun 30, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 80zvsblobprodcus35.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/razor/razor/.dotnet/dotnet msbuild /m /nologo /clp:Summary /v:minimal /nr:true /warnaserror /p:TreatWarningsAsErrors=true /p:ContinuousIntegrationBuild=false /home/REDACTED/work/razor/razor/artifacts/toolset/restore.proj /t:__WriteToolsetLocation /clp:ErrorsOnly;NoSummary /p:__ToolsetLocationOutputFile=/home/REDACTED/work/razor/razor/artifacts/toolset/9.0.0-beta.25255.5.txt (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from davidwengier June 30, 2025 12:00
davidwengier added a commit that referenced this pull request Jul 3, 2025
Fixes #11993
Part of #9519

Copilot [did the boring
work](#11994), I just did a bit of
clean up and testing. Left its commit history intact to preserve credit
:)
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.

Port Wrap with Tag endpoint to cohosting

2 participants