From 4f76753c37b4e9eae254b7cf362a51b3b0c0a5b8 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 16 Jul 2024 13:36:18 +1000 Subject: [PATCH 1/3] Allow creating an always active LSP server Also moved files needed for testing into a testing folder so we don't get them mixed up --- ...stractRazorLanguageServerFactoryWrapper.cs | 0 .../IRazorTestCapabilitiesProvider.cs | 0 .../{ => Testing}/RazorTestAnalyzerLoader.cs | 0 .../RazorTestLanguageServerFactory.cs} | 27 ++++++++++--------- .../RazorTestWorkspaceRegistrationService.cs | 0 5 files changed, 14 insertions(+), 13 deletions(-) rename src/Tools/ExternalAccess/Razor/{ => Testing}/AbstractRazorLanguageServerFactoryWrapper.cs (100%) rename src/Tools/ExternalAccess/Razor/{ => Testing}/IRazorTestCapabilitiesProvider.cs (100%) rename src/Tools/ExternalAccess/Razor/{ => Testing}/RazorTestAnalyzerLoader.cs (100%) rename src/Tools/ExternalAccess/Razor/{RazorLanguageServerFactoryWrapper.cs => Testing/RazorTestLanguageServerFactory.cs} (76%) rename src/Tools/ExternalAccess/Razor/{ => Testing}/RazorTestWorkspaceRegistrationService.cs (100%) diff --git a/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/Testing/AbstractRazorLanguageServerFactoryWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs rename to src/Tools/ExternalAccess/Razor/Testing/AbstractRazorLanguageServerFactoryWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/IRazorTestCapabilitiesProvider.cs b/src/Tools/ExternalAccess/Razor/Testing/IRazorTestCapabilitiesProvider.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/IRazorTestCapabilitiesProvider.cs rename to src/Tools/ExternalAccess/Razor/Testing/IRazorTestCapabilitiesProvider.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs b/src/Tools/ExternalAccess/Razor/Testing/RazorTestAnalyzerLoader.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs rename to src/Tools/ExternalAccess/Razor/Testing/RazorTestAnalyzerLoader.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/Testing/RazorTestLanguageServerFactory.cs similarity index 76% rename from src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs rename to src/Tools/ExternalAccess/Razor/Testing/RazorTestLanguageServerFactory.cs index 78104699f3f69..1d034b72bdbdb 100644 --- a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/Testing/RazorTestLanguageServerFactory.cs @@ -15,28 +15,29 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { - [Export(typeof(AbstractRazorLanguageServerFactoryWrapper))] [Shared] - internal class RazorLanguageServerFactoryWrapper : AbstractRazorLanguageServerFactoryWrapper + [Export(typeof(RazorTestLanguageServerFactory))] + [Export(typeof(AbstractRazorLanguageServerFactoryWrapper))] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + [method: ImportingConstructor] + internal class RazorTestLanguageServerFactory(ILanguageServerFactory languageServerFactory) : AbstractRazorLanguageServerFactoryWrapper { - private readonly ILanguageServerFactory _languageServerFactory; + private readonly ILanguageServerFactory _languageServerFactory = languageServerFactory; - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - [ImportingConstructor] - public RazorLanguageServerFactoryWrapper(ILanguageServerFactory languageServerFactory) + internal override IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices) { - if (languageServerFactory is null) - { - throw new ArgumentNullException(nameof(languageServerFactory)); - } + return CreateLanguageServerCore(jsonRpc, options, razorCapabilitiesProvider, hostServices, WellKnownLspServerKinds.RazorLspServer); + } - _languageServerFactory = languageServerFactory; + internal IRazorLanguageServerTarget CreateAlwaysActiveVSLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices) + { + return CreateLanguageServerCore(jsonRpc, options, razorCapabilitiesProvider, hostServices, WellKnownLspServerKinds.AlwaysActiveVSLspServer); } - internal override IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices) + private IRazorLanguageServerTarget CreateLanguageServerCore(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices, WellKnownLspServerKinds serverKind) { var capabilitiesProvider = new RazorCapabilitiesProvider(razorCapabilitiesProvider, options); - var languageServer = _languageServerFactory.Create(jsonRpc, options, capabilitiesProvider, WellKnownLspServerKinds.RazorLspServer, NoOpLspLogger.Instance, hostServices); + var languageServer = _languageServerFactory.Create(jsonRpc, options, capabilitiesProvider, serverKind, NoOpLspLogger.Instance, hostServices); return new RazorLanguageServerTargetWrapper(languageServer); } diff --git a/src/Tools/ExternalAccess/Razor/RazorTestWorkspaceRegistrationService.cs b/src/Tools/ExternalAccess/Razor/Testing/RazorTestWorkspaceRegistrationService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorTestWorkspaceRegistrationService.cs rename to src/Tools/ExternalAccess/Razor/Testing/RazorTestWorkspaceRegistrationService.cs From 2d311bd0fa69603143b1acfc2455865f5460309c Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 16 Jul 2024 13:36:24 +1000 Subject: [PATCH 2/3] Cleanup --- src/LanguageServer/Protocol/WellKnownLspServerKinds.cs | 5 ----- .../Razor/Cohost/AbstractRazorRequestHandler.cs | 6 ------ 2 files changed, 11 deletions(-) diff --git a/src/LanguageServer/Protocol/WellKnownLspServerKinds.cs b/src/LanguageServer/Protocol/WellKnownLspServerKinds.cs index a837cd105e93a..a99f84b743393 100644 --- a/src/LanguageServer/Protocol/WellKnownLspServerKinds.cs +++ b/src/LanguageServer/Protocol/WellKnownLspServerKinds.cs @@ -8,11 +8,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer; internal enum WellKnownLspServerKinds { - /// - /// Razor LSP server for Razor document requests (.razor and .cshtml files) - /// - RazorCohostServer, - /// /// Roslyn LSP server for razor c# requests. /// diff --git a/src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorRequestHandler.cs b/src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorRequestHandler.cs index 33492c7b05fa7..df0a5899bb4fe 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorRequestHandler.cs +++ b/src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorRequestHandler.cs @@ -17,12 +17,6 @@ internal abstract class AbstractRazorCohostRequestHandler IRequestHandler.HandleRequestAsync(TRequestType request, RequestContext context, CancellationToken cancellationToken) { - // We have to wrap the RequestContext in order to expose it to Roslyn. We could create our own (by exporting - // and IRequestContextFactory) but that would not be possible if/when we live in the same server as Roslyn - // so may as well deal with it now. - // This does mean we can't nicely pass through the original Uri, which would have ProjectContext info, but - // we get the Project so that will have to do. - var razorRequestContext = new RazorCohostRequestContext(context); return HandleRequestAsync(request, razorRequestContext, cancellationToken); } From d065d9cbd508009bd66c1de51491574963556d64 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 16 Jul 2024 14:01:18 +1000 Subject: [PATCH 3/3] Allow Razor tests to utilize RazorPinnedSolutionInfoWrapper in tests --- .../Razor/Testing/TestSolutionStore.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/Tools/ExternalAccess/Razor/Testing/TestSolutionStore.cs diff --git a/src/Tools/ExternalAccess/Razor/Testing/TestSolutionStore.cs b/src/Tools/ExternalAccess/Razor/Testing/TestSolutionStore.cs new file mode 100644 index 0000000000000..bd8f3f35b61e3 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Testing/TestSolutionStore.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal sealed class TestSolutionStore +{ + private readonly Dictionary _solutions = []; + + internal async Task AddAsync(Solution solution, CancellationToken cancellationToken) + { + // Using compilation state, since that is what is used in the real SolutionAssetStorage class + // Compilation state is the SolutionState checksum, plus source generator info, which seems pretty relevant :) + var checksum = await solution.CompilationState.GetChecksumAsync(cancellationToken).ConfigureAwait(false); + + lock (_solutions) + { + if (_solutions.TryGetValue(checksum, out var existingSolution)) + { + return checksum; + } + + _solutions.Add(checksum, solution); + } + + return checksum; + } + + internal Solution? Get(RazorPinnedSolutionInfoWrapper solutionInfo) + { + lock (_solutions) + { + _solutions.TryGetValue(solutionInfo.UnderlyingObject, out var solution); + + return solution; + } + } +}