Skip to content

Commit 3cc5eb9

Browse files
committed
Only preinitialize when the UI context is active
1 parent 6b2b0e4 commit 3cc5eb9

File tree

2 files changed

+37
-17
lines changed

2 files changed

+37
-17
lines changed

src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ internal static class Constants
1111
{
1212
public const string RazorLanguageName = LanguageInfoProvider.RazorLanguageName;
1313

14-
// The UI context is provided by Razor, so this guid must match the one in https://github.com/dotnet/razor/blob/main/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorConstants.cs
14+
// These UI contexts are provided by Razor, so must match https://github.com/dotnet/razor/blob/main/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorConstants.cs
1515
public static readonly Guid RazorCohostingUIContext = new Guid("6d5b86dc-6b8a-483b-ae30-098a3c7d6774");
16+
17+
public static readonly Guid RazorCapabilityPresentUIContext = new Guid("2077a158-ee71-484c-be76-350a1d49eaea");
1618
}

src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,55 +38,73 @@ private class RazorStartupService(
3838
AbstractRazorCohostLifecycleService? razorCohostLifecycleService) : ILspService, IOnInitialized, IDisposable
3939
{
4040
private readonly CancellationTokenSource _disposalTokenSource = new();
41-
private IDisposable? _activation;
41+
private IDisposable? _cohostActivation;
42+
private IDisposable? _razorFilePresentActivation;
4243

4344
public void Dispose()
4445
{
4546
razorCohostLifecycleService?.Dispose();
4647

47-
_activation?.Dispose();
48-
_activation = null;
48+
_razorFilePresentActivation?.Dispose();
49+
_razorFilePresentActivation = null;
50+
_cohostActivation?.Dispose();
51+
_cohostActivation = null;
4952
_disposalTokenSource.Cancel();
5053
}
5154

52-
public async Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken)
55+
public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken)
5356
{
5457
if (context.ServerKind is not (WellKnownLspServerKinds.AlwaysActiveVSLspServer or WellKnownLspServerKinds.CSharpVisualBasicLspServer))
5558
{
5659
// We have to register this class for Any server, but only want to run in the C# server in VS or VS Code
57-
return;
60+
return Task.CompletedTask;
5861
}
5962

6063
if (cohostStartupService is null && razorCohostLifecycleService is null)
6164
{
62-
return;
63-
}
64-
65-
if (razorCohostLifecycleService is not null)
66-
{
67-
// If we have a cohost lifecycle service, fire pre-initialization, which happens when the LSP server starts up, but before
68-
// the UIContext is activated.
69-
await razorCohostLifecycleService.LspServerIntializedAsync(cancellationToken).ConfigureAwait(false);
65+
return Task.CompletedTask;
7066
}
7167

7268
if (uIContextActivationService is null)
7369
{
74-
// Outside of VS, we want to initialize immediately.. I think?
70+
PreinitializeRazor();
7571
InitializeRazor();
7672
}
7773
else
7874
{
79-
_activation = uIContextActivationService.ExecuteWhenActivated(Constants.RazorCohostingUIContext, InitializeRazor);
75+
// There are two initialization methods for Razor, which looks odd here, but are really controlled by UI contexts.
76+
// This method fires for any Roslyn project, but not all Roslyn projects are Razor projects, so the first UI context
77+
// triggers where there is a project with a Razor capability present in the solution, and the next is when a Razor file
78+
// is opened in the editor. ie these two lines look the same, but really they do different levels of initialization.
79+
_razorFilePresentActivation = uIContextActivationService.ExecuteWhenActivated(Constants.RazorCapabilityPresentUIContext, PreinitializeRazor);
80+
_cohostActivation = uIContextActivationService.ExecuteWhenActivated(Constants.RazorCohostingUIContext, InitializeRazor);
8081
}
8182

82-
return;
83+
return Task.CompletedTask;
84+
85+
void PreinitializeRazor()
86+
{
87+
this.PreinitializeRazorAsync(_disposalTokenSource.Token).ReportNonFatalErrorAsync();
88+
}
8389

8490
void InitializeRazor()
8591
{
8692
this.InitializeRazorAsync(clientCapabilities, context, _disposalTokenSource.Token).ReportNonFatalErrorAsync();
8793
}
8894
}
8995

96+
private async Task PreinitializeRazorAsync(CancellationToken cancellationToken)
97+
{
98+
if (cancellationToken.IsCancellationRequested) return;
99+
100+
await TaskScheduler.Default.SwitchTo(alwaysYield: true);
101+
102+
if (razorCohostLifecycleService is not null)
103+
{
104+
await razorCohostLifecycleService.LspServerIntializedAsync(cancellationToken).ConfigureAwait(false);
105+
}
106+
}
107+
90108
private async Task InitializeRazorAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken)
91109
{
92110
// The LSP server will dispose us when the server exits, but VS could decide to activate us later.

0 commit comments

Comments
 (0)