Skip to content

Commit 3cd939f

Browse files
authored
Merge pull request #71671 from davidwengier/NewRazorCohostAPI
[Razor Cohost] Pass TextDocuments to Razor for text sync LSP messages
2 parents 1ce5f19 + 872a49e commit 3cd939f

File tree

4 files changed

+61
-16
lines changed

4 files changed

+61
-16
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.LanguageServer.Handler;
9+
using Microsoft.CommonLanguageServerProtocol.Framework;
10+
using Roslyn.LanguageServer.Protocol;
11+
12+
namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
13+
14+
internal interface IRazorCohostTextDocumentSyncHandler
15+
{
16+
Task HandleAsync(int version, RazorCohostRequestContext context, CancellationToken cancellationToken);
17+
}
18+
19+
internal static class IRazorCohostTextDocumentSyncHandlerExtensions
20+
{
21+
public static async Task NotifyRazorAsync(this IRazorCohostTextDocumentSyncHandler? openOrChangeHandler, Uri uri, int version, RequestContext context, CancellationToken cancellationToken)
22+
{
23+
if (openOrChangeHandler is null)
24+
return;
25+
26+
// Razor is a little special here, because when a .razor or .cshtml document is opened/changed, which is what this request is for,
27+
// they need to generate a C# and/or Html document. To do this they use the Razor Source Generator, but to run the generator they
28+
// need a TextDocument for the Razor file that this request is for. To facilitate that need we create a RequestContext here
29+
// and pass it to Razor, which is essentially the same as the RequestContext they would get on the next request, but by providing it
30+
// early here, they can do their generation before the didOpen/didChange/didClose mutating request is finished.
31+
// This is a little hacky, but it's the best we can do for now. In future hopefully we can switch to a pull model where the source
32+
// generator just provides C# source to the project/compilation as normal. Whether we need to maintain this system for the Html
33+
// generated documents remains to be seen.
34+
var clientCapabilitiesManager = context.GetRequiredService<IInitializeManager>();
35+
var clientCapabilities = clientCapabilitiesManager.TryGetClientCapabilities();
36+
var logger = context.GetRequiredService<AbstractLspLogger>();
37+
var serverInfoProvider = context.GetRequiredService<ServerInfoProvider>();
38+
var supportedLanguages = serverInfoProvider.SupportedLanguages;
39+
var lspServices = context.GetRequiredService<ILspServices>();
40+
41+
var newContext = await RequestContext.CreateAsync(false, true, new TextDocumentIdentifier() { Uri = uri }, LanguageServer.WellKnownLspServerKinds.RazorCohostServer, clientCapabilities, supportedLanguages, lspServices, logger, context.Method, cancellationToken).ConfigureAwait(false);
42+
43+
var razorContext = new RazorCohostRequestContext(newContext);
44+
45+
await openOrChangeHandler.HandleAsync(version, razorContext, cancellationToken).ConfigureAwait(false);
46+
}
47+
}

src/Tools/ExternalAccess/Razor/Cohost/RazorCohostDidChangeEndpoint.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
1818
[ExportRazorStatelessLspService(typeof(RazorCohostDidChangeEndpoint)), Shared]
1919
[method: ImportingConstructor]
2020
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
21-
internal sealed class RazorCohostDidChangeEndpoint([Import(AllowDefault = true)] IRazorCohostDidChangeHandler? didChangeHandler) : ILspServiceDocumentRequestHandler<DidChangeTextDocumentParams, object?>
21+
internal sealed class RazorCohostDidChangeEndpoint(
22+
[Import(AllowDefault = true)] IRazorCohostTextDocumentSyncHandler? razorDocSyncHandler)
23+
: ILspServiceDocumentRequestHandler<DidChangeTextDocumentParams, object?>
2224
{
2325
public bool MutatesSolutionState => true;
2426
public bool RequiresLSPSolution => false;
@@ -40,15 +42,13 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(DidChangeTextDocumentPar
4042
context.UpdateTrackedDocument(request.TextDocument.Uri, text);
4143

4244
// Razor can't handle this request because they don't have access to the RequestContext, but they might want to do something with it
43-
if (didChangeHandler is not null)
44-
{
45-
await didChangeHandler.HandleAsync(request.TextDocument.Uri, request.TextDocument.Version, text, cancellationToken).ConfigureAwait(false);
46-
}
45+
await razorDocSyncHandler.NotifyRazorAsync(request.TextDocument.Uri, request.TextDocument.Version, context, cancellationToken).ConfigureAwait(false);
4746

4847
return null;
4948
}
5049
}
5150

51+
[Obsolete("This API is made of regret, no longer functions, and will be removed very soon")]
5252
internal interface IRazorCohostDidChangeHandler
5353
{
5454
Task HandleAsync(Uri uri, int version, SourceText sourceText, CancellationToken cancellationToken);

src/Tools/ExternalAccess/Razor/Cohost/RazorCohostDidCloseEndpoint.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
1717
[ExportRazorStatelessLspService(typeof(RazorCohostDidCloseEndpoint)), Shared]
1818
[method: ImportingConstructor]
1919
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
20-
internal sealed class RazorCohostDidCloseEndpoint([Import(AllowDefault = true)] IRazorCohostDidCloseHandler? didCloseHandler) : ILspServiceNotificationHandler<DidCloseTextDocumentParams>, ITextDocumentIdentifierHandler<DidCloseTextDocumentParams, TextDocumentIdentifier>
20+
internal sealed class RazorCohostDidCloseEndpoint(
21+
[Import(AllowDefault = true)] IRazorCohostTextDocumentSyncHandler? razorDocSyncHandler)
22+
: ILspServiceNotificationHandler<DidCloseTextDocumentParams>, ITextDocumentIdentifierHandler<DidCloseTextDocumentParams, TextDocumentIdentifier>
2123
{
2224
public bool MutatesSolutionState => true;
2325
public bool RequiresLSPSolution => false;
@@ -32,13 +34,11 @@ public async Task HandleNotificationAsync(DidCloseTextDocumentParams request, Re
3234
await context.StopTrackingAsync(request.TextDocument.Uri, cancellationToken).ConfigureAwait(false);
3335

3436
// Razor can't handle this request because they don't have access to the RequestContext, but they might want to do something with it
35-
if (didCloseHandler is not null)
36-
{
37-
await didCloseHandler.HandleAsync(request.TextDocument.Uri, cancellationToken).ConfigureAwait(false);
38-
}
37+
await razorDocSyncHandler.NotifyRazorAsync(request.TextDocument.Uri, version: -1, context, cancellationToken).ConfigureAwait(false);
3938
}
4039
}
4140

41+
[Obsolete("This API is made of regret, no longer functions, and will be removed very soon")]
4242
internal interface IRazorCohostDidCloseHandler
4343
{
4444
Task HandleAsync(Uri uri, CancellationToken cancellationToken);

src/Tools/ExternalAccess/Razor/Cohost/RazorCohostDidOpenEndpoint.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
1919
[method: ImportingConstructor]
2020
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
2121
internal sealed class RazorCohostDidOpenEndpoint(
22-
[Import(AllowDefault = true)] IRazorCohostDidOpenHandler? didOpenHandler)
22+
[Import(AllowDefault = true)] IRazorCohostTextDocumentSyncHandler? razorDocSyncHandler)
2323
: ILspServiceNotificationHandler<DidOpenTextDocumentParams>, ITextDocumentIdentifierHandler<DidOpenTextDocumentParams, Uri>
2424
{
2525
public bool MutatesSolutionState => true;
@@ -36,14 +36,12 @@ public async Task HandleNotificationAsync(DidOpenTextDocumentParams request, Req
3636

3737
await context.StartTrackingAsync(request.TextDocument.Uri, sourceText, request.TextDocument.LanguageId, cancellationToken).ConfigureAwait(false);
3838

39-
// Razor can't handle this request because they don't have access to the RequestContext, but they might want to do something with it
40-
if (didOpenHandler is not null)
41-
{
42-
await didOpenHandler.HandleAsync(request.TextDocument.Uri, request.TextDocument.Version, sourceText, cancellationToken).ConfigureAwait(false);
43-
}
39+
// Razor can't handle this request directly because they don't have access to the RequestContext, but they might want to do something with it
40+
await razorDocSyncHandler.NotifyRazorAsync(request.TextDocument.Uri, request.TextDocument.Version, context, cancellationToken).ConfigureAwait(false);
4441
}
4542
}
4643

44+
[Obsolete("This API is made of regret, no longer functions, and will be removed very soon")]
4745
internal interface IRazorCohostDidOpenHandler
4846
{
4947
Task HandleAsync(Uri uri, int version, SourceText sourceText, CancellationToken cancellationToken);

0 commit comments

Comments
 (0)