From 6ff656614e80cb0376bffcc9a5e965bb340d555f Mon Sep 17 00:00:00 2001 From: Alex Gavrilov Date: Thu, 31 Oct 2024 17:38:05 -0700 Subject: [PATCH] Adding client capabilities to OOP client initialization data (#11129) * Add RemoteClientCapabilities service The service gets initialized once during initial connection / init and provides client capabilities to other remote services. * Add RemoteClientCapabilitiesService - Adding client capabilities to RemoteClientLSPInitializationOptions - Converting IRemoteClientIntializationService to be a JSON service for simplicity of data serialization - Converting client initialization code to use JSON client to call IRemoteClientIntializationService * Fixing tests * Removing unneeded class * Fixing tests * Export IClientCapabilitiesService and consume it when appropriate Only consumers that are initializing capabiilities service by calling SetCapabilities should be importing it via RemoteClientCapabilitiesService. All other consumers should be using IClientCapabilitiesService. * Simplifying code, moving and correcting comment * Test fixup per PR feedback * Service rename per PR feedback * More PR feedback - Renaming a variable - Made UpdateClientLSPInitializationOptions mode complete (so it updates RemoteSemanticTokensLegendService now with initialization data) - Removed limitation of single update on RemoteSemanticTokensLegendService - Use UpdateClientLSPInitializationOptions in cohost semantic tokens test * Removed unused service --- .../AbstractClientCapabilitiesService.cs | 21 ++++++++++++ .../IRemoteClientInitializationService.cs | 2 +- .../Remote/RazorServices.cs | 2 +- .../RemoteClientInitializationOptions.cs | 31 +++++++++-------- .../RemoteClientLSPInitializationOptions.cs | 15 +++++---- .../RemoteDocumentSymbolService.cs | 5 +-- .../RemoteClientCapabilitiesService.cs | 12 +++++++ .../RemoteClientInitializationService.cs | 2 ++ .../RemoteSemanticTokensLegendService.cs | 7 ++-- .../RazorCohostClientCapabilitiesService.cs | 16 +-------- .../Remote/RemoteServiceInvoker.cs | 33 ++++++++++--------- .../RazorServicesTest.cs | 8 +---- .../Cohost/CohostEndpointTestBase.cs | 23 +++++++++++++ .../CohostSemanticTokensRangeEndpointTest.cs | 4 +-- 14 files changed, 111 insertions(+), 70 deletions(-) create mode 100644 src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/AbstractClientCapabilitiesService.cs create mode 100644 src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientCapabilitiesService.cs diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/AbstractClientCapabilitiesService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/AbstractClientCapabilitiesService.cs new file mode 100644 index 00000000000..c79fa014352 --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/AbstractClientCapabilitiesService.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.Razor.Protocol; + +internal abstract class AbstractClientCapabilitiesService : IClientCapabilitiesService +{ + private VSInternalClientCapabilities? _clientCapabilities; + + public bool CanGetClientCapabilities => _clientCapabilities is not null; + + public VSInternalClientCapabilities ClientCapabilities => _clientCapabilities ?? throw new InvalidOperationException("Client capabilities requested before initialized."); + + public void SetCapabilities(VSInternalClientCapabilities clientCapabilities) + { + _clientCapabilities = clientCapabilities; + } +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteClientInitializationService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteClientInitializationService.cs index 8089bc68099..3fde6ad6144 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteClientInitializationService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteClientInitializationService.cs @@ -6,7 +6,7 @@ namespace Microsoft.CodeAnalysis.Razor.Remote; -internal interface IRemoteClientInitializationService +internal interface IRemoteClientInitializationService : IRemoteJsonService { ValueTask InitializeAsync(RemoteClientInitializationOptions initializationOptions, CancellationToken cancellationToken); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs index 2e4257cdb3c..0fb7d6c8596 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs @@ -15,7 +15,6 @@ internal static class RazorServices [ (typeof(IRemoteLinkedEditingRangeService), null), (typeof(IRemoteTagHelperProviderService), null), - (typeof(IRemoteClientInitializationService), null), (typeof(IRemoteSemanticTokensService), null), (typeof(IRemoteHtmlDocumentService), null), (typeof(IRemoteUriPresentationService), null), @@ -29,6 +28,7 @@ internal static class RazorServices // Internal for testing internal static readonly IEnumerable<(Type, Type?)> JsonServices = [ + (typeof(IRemoteClientInitializationService), null), (typeof(IRemoteGoToDefinitionService), null), (typeof(IRemoteSignatureHelpService), null), (typeof(IRemoteInlayHintService), null), diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientInitializationOptions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientInitializationOptions.cs index 0ca48ba1a20..5bd3a09b409 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientInitializationOptions.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientInitializationOptions.cs @@ -1,31 +1,30 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.Razor.Remote; -[DataContract] internal struct RemoteClientInitializationOptions { - [DataMember(Order = 0)] - internal required bool UseRazorCohostServer; + [JsonPropertyName("useRazorCohostServer")] + public required bool UseRazorCohostServer { get; set; } - [DataMember(Order = 1)] - internal required bool UsePreciseSemanticTokenRanges; + [JsonPropertyName("usePreciseSemanticTokenRanges")] + public required bool UsePreciseSemanticTokenRanges { get; set; } - [DataMember(Order = 2)] - internal required string CSharpVirtualDocumentSuffix; + [JsonPropertyName("csharpVirtualDocumentSuffix")] + public required string CSharpVirtualDocumentSuffix { get; set; } - [DataMember(Order = 3)] - internal required string HtmlVirtualDocumentSuffix; + [JsonPropertyName("htmlVirtualDocumentSuffix")] + public required string HtmlVirtualDocumentSuffix { get; set; } - [DataMember(Order = 4)] - internal required bool IncludeProjectKeyInGeneratedFilePath; + [JsonPropertyName("includeProjectKeyInGeneratedFilePath")] + public required bool IncludeProjectKeyInGeneratedFilePath { get; set; } - [DataMember(Order = 5)] - internal required bool ReturnCodeActionAndRenamePathsWithPrefixedSlash; + [JsonPropertyName("returnCodeActionAndRenamePathsWithPrefixedSlash")] + public required bool ReturnCodeActionAndRenamePathsWithPrefixedSlash { get; set; } - [DataMember(Order = 6)] - internal required bool ForceRuntimeCodeGeneration; + [JsonPropertyName("forceRuntimeCodeGeneration")] + public required bool ForceRuntimeCodeGeneration { get; set; } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientLSPInitializationOptions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientLSPInitializationOptions.cs index 3a666586434..5c74a839ecc 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientLSPInitializationOptions.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RemoteClientLSPInitializationOptions.cs @@ -1,16 +1,19 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.Razor.Remote; -[DataContract] internal struct RemoteClientLSPInitializationOptions { - [DataMember(Order = 0)] - internal required string[] TokenTypes; + [JsonPropertyName("tokenTypes")] + public required string[] TokenTypes { get; set; } - [DataMember(Order = 1)] - internal required string[] TokenModifiers; + [JsonPropertyName("tokenModifiers")] + public required string[] TokenModifiers { get; set; } + + [JsonPropertyName("clientCapabilities")] + public required VSInternalClientCapabilities ClientCapabilities { get; set; } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/DocumentSymbols/RemoteDocumentSymbolService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/DocumentSymbols/RemoteDocumentSymbolService.cs index 109ff606ee2..7dd6aae79ef 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/DocumentSymbols/RemoteDocumentSymbolService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/DocumentSymbols/RemoteDocumentSymbolService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Razor.Protocol; using Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols; using Microsoft.CodeAnalysis.Razor.Remote; using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; @@ -23,6 +24,7 @@ protected override IRemoteDocumentSymbolService CreateService(in ServiceArgs arg } private readonly IDocumentSymbolService _documentSymbolService = args.ExportProvider.GetExportedValue(); + private readonly IClientCapabilitiesService _clientCapabilitiesService = args.ExportProvider.GetExportedValue(); public ValueTask?> GetDocumentSymbolsAsync(JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, JsonSerializableDocumentId razorDocumentId, bool useHierarchicalSymbols, CancellationToken cancellationToken) => RunServiceAsync( @@ -40,8 +42,7 @@ protected override IRemoteDocumentSymbolService CreateService(in ServiceArgs arg var csharpSymbols = await ExternalHandlers.DocumentSymbols.GetDocumentSymbolsAsync( generatedDocument, useHierarchicalSymbols, - // TODO: use correct value from client capabilities when https://github.com/dotnet/razor/issues/11102 - supportsVSExtensions: true, + supportsVSExtensions: _clientCapabilitiesService.ClientCapabilities.SupportsVisualStudioExtensions, cancellationToken).ConfigureAwait(false); var codeDocument = await context.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientCapabilitiesService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientCapabilitiesService.cs new file mode 100644 index 00000000000..3d07730d5dd --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientCapabilitiesService.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Composition; +using Microsoft.CodeAnalysis.Razor.Protocol; + +namespace Microsoft.CodeAnalysis.Remote.Razor; + +[Shared] +[Export(typeof(IClientCapabilitiesService))] +[Export(typeof(RemoteClientCapabilitiesService))] +internal sealed class RemoteClientCapabilitiesService : AbstractClientCapabilitiesService; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientInitializationService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientInitializationService.cs index 64ef061b7d1..adaf885937d 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientInitializationService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Initialization/RemoteClientInitializationService.cs @@ -16,6 +16,7 @@ protected override IRemoteClientInitializationService CreateService(in ServiceAr => new RemoteClientInitializationService(in args); } + private readonly RemoteClientCapabilitiesService _remoteClientCapabilitiesService = args.ExportProvider.GetExportedValue(); private readonly RemoteLanguageServerFeatureOptions _remoteLanguageServerFeatureOptions = args.ExportProvider.GetExportedValue(); private readonly RemoteSemanticTokensLegendService _remoteSemanticTokensLegendService = args.ExportProvider.GetExportedValue(); @@ -31,6 +32,7 @@ public ValueTask InitializeLSPAsync(RemoteClientLSPInitializationOptions options => RunServiceAsync(ct => { _remoteSemanticTokensLegendService.SetLegend(options.TokenTypes, options.TokenModifiers); + _remoteClientCapabilitiesService.SetCapabilities(options.ClientCapabilities); return default; }, cancellationToken); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteSemanticTokensLegendService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteSemanticTokensLegendService.cs index d5c3b6d2f54..4c3b8bafe6c 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteSemanticTokensLegendService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/SemanticTokens/RemoteSemanticTokensLegendService.cs @@ -20,10 +20,7 @@ internal sealed class RemoteSemanticTokensLegendService : ISemanticTokensLegendS public void SetLegend(string[] tokenTypes, string[] tokenModifiers) { - if (_tokenTypes is null) - { - _tokenTypes = new SemanticTokenTypes(tokenTypes); - _tokenModifiers = new SemanticTokenModifiers(tokenModifiers); - } + _tokenTypes = new SemanticTokenTypes(tokenTypes); + _tokenModifiers = new SemanticTokenModifiers(tokenModifiers); } } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/RazorCohostClientCapabilitiesService.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/RazorCohostClientCapabilitiesService.cs index cde4f43bbd8..7903d5b5dcd 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/RazorCohostClientCapabilitiesService.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/RazorCohostClientCapabilitiesService.cs @@ -1,25 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -using System; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.Razor.Protocol; -using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; [Export(typeof(RazorCohostClientCapabilitiesService))] [Export(typeof(IClientCapabilitiesService))] -internal class RazorCohostClientCapabilitiesService : IClientCapabilitiesService -{ - private VSInternalClientCapabilities? _clientCapabilities; - - public bool CanGetClientCapabilities => _clientCapabilities is not null; - - public VSInternalClientCapabilities ClientCapabilities => _clientCapabilities ?? throw new InvalidOperationException("Client capabilities requested before initialized."); - - public void SetCapabilities(VSInternalClientCapabilities clientCapabilities) - { - _clientCapabilities = clientCapabilities; - } -} +internal sealed class RazorCohostClientCapabilitiesService : AbstractClientCapabilitiesService; diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Remote/RemoteServiceInvoker.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Remote/RemoteServiceInvoker.cs index 1e2e54bcc46..02ed22ce44a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Remote/RemoteServiceInvoker.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Remote/RemoteServiceInvoker.cs @@ -81,6 +81,13 @@ internal sealed class RemoteServiceInvoker( private async Task TryGetClientAsync(CancellationToken cancellationToken) { + // Even if we're getting a service that wants to use MessagePack, we still have to initialize the OOP client + // so we get the JSON client too and use it to call initialization service + if (!_fullyInitialized) + { + _ = await TryGetJsonClientAsync(cancellationToken).ConfigureAwait(false); + } + var workspace = _workspaceProvider.GetWorkspace(); var remoteClient = await RazorRemoteHostClient.TryGetClientAsync( @@ -89,32 +96,27 @@ internal sealed class RemoteServiceInvoker( RazorRemoteServiceCallbackDispatcherRegistry.Empty, cancellationToken).ConfigureAwait(false); - if (remoteClient is null) - { - return null; - } - - await InitializeRemoteClientAsync(remoteClient, cancellationToken).ConfigureAwait(false); - return remoteClient; } private async Task TryGetJsonClientAsync(CancellationToken cancellationToken) { - // Even if we're getting a service that wants to use Json, we still have to initialize the OOP client - // so we get the regular (MessagePack) client too. - if (!_fullyInitialized) - { - _ = await TryGetClientAsync(cancellationToken).ConfigureAwait(false); - } - var workspace = _workspaceProvider.GetWorkspace(); - return await RazorRemoteHostClient.TryGetClientAsync( + var remoteClient = await RazorRemoteHostClient.TryGetClientAsync( workspace.Services, RazorServices.JsonDescriptors, RazorRemoteServiceCallbackDispatcherRegistry.Empty, cancellationToken).ConfigureAwait(false); + + if (remoteClient is null) + { + return null; + } + + await InitializeRemoteClientAsync(remoteClient, cancellationToken).ConfigureAwait(false); + + return remoteClient; } private async Task InitializeRemoteClientAsync(RazorRemoteHostClient remoteClient, CancellationToken cancellationToken) @@ -157,6 +159,7 @@ private async Task InitializeRemoteClientAsync(RazorRemoteHostClient remoteClien { var initParams = new RemoteClientLSPInitializationOptions { + ClientCapabilities = _clientCapabilitiesService.ClientCapabilities, TokenTypes = _semanticTokensLegendService.TokenTypes.All, TokenModifiers = _semanticTokensLegendService.TokenModifiers.All, }; diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Remote.Razor.Test/RazorServicesTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Remote.Razor.Test/RazorServicesTest.cs index 4b4a579532c..27488a55eed 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Remote.Razor.Test/RazorServicesTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Remote.Razor.Test/RazorServicesTest.cs @@ -45,7 +45,6 @@ public void JsonServicesHaveTheRightParameters(Type serviceType, Type? _) { Assert.True(typeof(IRemoteJsonService).IsAssignableFrom(serviceType)); - var found = false; foreach (var method in serviceType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { if (method.Name != "RunServiceAsync" && @@ -55,14 +54,9 @@ public void JsonServicesHaveTheRightParameters(Type serviceType, Type? _) { Assert.Fail($"Method {method.Name} in a Json service has a pinned solution info wrapper parameter that isn't Json serializable"); } - else if (typeof(JsonSerializableRazorPinnedSolutionInfoWrapper).IsAssignableFrom(parameterType)) - { - found = true; - } + } } - - Assert.True(found, "Didn't find a method to validate, which means maybe this test is invalid"); } [Fact] diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTestBase.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTestBase.cs index 1a49037fb60..cc72ed18a4c 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTestBase.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTestBase.cs @@ -17,8 +17,10 @@ using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.CodeAnalysis.Remote.Razor; using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; +using Microsoft.CodeAnalysis.Remote.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Composition; +using Microsoft.VisualStudio.LanguageServer.Protocol; using Xunit.Abstractions; namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; @@ -29,11 +31,14 @@ public abstract class CohostEndpointTestBase(ITestOutputHelper testOutputHelper) private ExportProvider? _exportProvider; private TestRemoteServiceInvoker? _remoteServiceInvoker; private RemoteClientInitializationOptions _clientInitializationOptions; + private RemoteClientLSPInitializationOptions _clientLSPInitializationOptions; private IFilePathService? _filePathService; private protected TestRemoteServiceInvoker RemoteServiceInvoker => _remoteServiceInvoker.AssumeNotNull(); private protected IFilePathService FilePathService => _filePathService.AssumeNotNull(); private protected RemoteLanguageServerFeatureOptions FeatureOptions => OOPExportProvider.GetExportedValue(); + private protected RemoteClientCapabilitiesService ClientCapabilities => OOPExportProvider.GetExportedValue(); + private protected RemoteSemanticTokensLegendService SemanticTokensLegendService => OOPExportProvider.GetExportedValue(); /// /// The export provider for Razor OOP services (not Roslyn) @@ -64,6 +69,17 @@ protected override async Task InitializeAsync() }; UpdateClientInitializationOptions(c => c); + _clientLSPInitializationOptions = new() + { + ClientCapabilities = new VSInternalClientCapabilities() + { + SupportsVisualStudioExtensions = true + }, + TokenTypes = [], + TokenModifiers = [] + }; + UpdateClientLSPInitializationOptions(c => c); + _filePathService = new RemoteFilePathService(FeatureOptions); } @@ -73,6 +89,13 @@ private protected void UpdateClientInitializationOptions(Func mutation) + { + _clientLSPInitializationOptions = mutation(_clientLSPInitializationOptions); + ClientCapabilities.SetCapabilities(_clientLSPInitializationOptions.ClientCapabilities); + SemanticTokensLegendService.SetLegend(_clientLSPInitializationOptions.TokenTypes, _clientLSPInitializationOptions.TokenModifiers); + } + protected Task CreateProjectAndRazorDocumentAsync( string contents, string? fileKind = null, diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs index 794cfc67ff1..be4fe9f5f05 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.LanguageServer.Semantic; using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.AspNetCore.Razor.Telemetry; +using Microsoft.CodeAnalysis.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Razor.Settings; using Microsoft.CodeAnalysis.Remote.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Text; @@ -97,8 +98,7 @@ private async Task VerifySemanticTokensAsync(string input, bool colorBackground, var legend = TestRazorSemanticTokensLegendService.Instance; // We need to manually initialize the OOP service so we can get semantic token info later - var legendService = OOPExportProvider.GetExportedValue(); - legendService.SetLegend(legend.TokenTypes.All, legend.TokenModifiers.All); + UpdateClientLSPInitializationOptions(options => options with { TokenTypes = legend.TokenTypes.All, TokenModifiers = legend.TokenModifiers.All }); // Update the client initialization options to control the precise ranges option UpdateClientInitializationOptions(c => c with { UsePreciseSemanticTokenRanges = precise });