diff --git a/Razor.sln b/Razor.sln index fe6f4f71aaa..046867b2eb8 100644 --- a/Razor.sln +++ b/Razor.sln @@ -142,6 +142,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.CodeAnalysis.Razo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Razor.Compiler", "src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj", "{E102632D-EC30-474F-B2EB-6EB07ED45F27}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Razor.CohostingShared", "src\Razor\src\Microsoft.CodeAnalysis.Razor.CohostingShared\Microsoft.CodeAnalysis.Razor.CohostingShared.shproj", "{6778F3D4-C6C2-4882-9F86-54FCD44B078A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -587,15 +589,19 @@ Global {094A024C-7DC1-487B-9F52-9D65CDFEE18D} = {92463391-81BE-462B-AC3C-78C6C760741F} {440580F4-D746-49BB-AE8E-9F898483EA75} = {5B60F564-4AD7-4B70-A887-7D91496799A2} {E102632D-EC30-474F-B2EB-6EB07ED45F27} = {440580F4-D746-49BB-AE8E-9F898483EA75} + {6778F3D4-C6C2-4882-9F86-54FCD44B078A} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0035341D-175A-4D05-95E6-F1C2785A1E26} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\Microsoft.AspNetCore.Razor.Serialization.Json\Microsoft.AspNetCore.Razor.Serialization.Json.projitems*{078aef36-f319-4ce2-baa2-5b58a6536b46}*SharedItemsImports = 5 + src\Razor\src\Microsoft.CodeAnalysis.Razor.CohostingShared\Microsoft.CodeAnalysis.Razor.CohostingShared.projitems*{2223b8fd-d98a-47be-94a9-6a3a6b8557b8}*SharedItemsImports = 5 src\Shared\Microsoft.AspNetCore.Razor.Serialization.Json\Microsoft.AspNetCore.Razor.Serialization.Json.projitems*{6205467f-e381-4c42-aeec-763bd62b3d5e}*SharedItemsImports = 5 + src\Razor\src\Microsoft.CodeAnalysis.Razor.CohostingShared\Microsoft.CodeAnalysis.Razor.CohostingShared.projitems*{6778f3d4-c6c2-4882-9f86-54fcd44b078a}*SharedItemsImports = 13 src\Shared\Microsoft.AspNetCore.Razor.Serialization.Json\Microsoft.AspNetCore.Razor.Serialization.Json.projitems*{7b1c5668-b4bc-45d9-ae4c-9c2382fc47a9}*SharedItemsImports = 5 src\Shared\Microsoft.AspNetCore.Razor.Serialization.Json\Microsoft.AspNetCore.Razor.Serialization.Json.projitems*{cd6913f3-ec47-4470-9c45-f5f898615e9d}*SharedItemsImports = 13 + src\Razor\src\Microsoft.CodeAnalysis.Razor.CohostingShared\Microsoft.CodeAnalysis.Razor.CohostingShared.projitems*{e5d92db7-5cbf-410a-9685-ff76f71ec96f}*SharedItemsImports = 5 src\Shared\Microsoft.AspNetCore.Razor.Serialization.Json\Microsoft.AspNetCore.Razor.Serialization.Json.projitems*{e5d92db7-5cbf-410a-9685-ff76f71ec96f}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f5de9cfdb7b..5cfd70d6852 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -7,103 +7,99 @@ 7dbf5deea5bdccf513df73cba179c4c0ad106010 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 09312a65870e883999ffe2ca25edd05cbc241e05 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 - - - https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 - + https://github.com/dotnet/roslyn - 605d9346cdae106907f67663250788eff6048b09 + 05e49aa98995349ffa26a19020333293ffe99670 diff --git a/eng/Versions.props b/eng/Versions.props index 6d44c4e1c34..854fbc5afa8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -47,32 +47,31 @@ --> - 3.12.0-beta1.25209.6 + 3.12.0-beta1.25222.12 9.0.0-alpha.1.25209.1 9.0.0-beta.25208.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25207.1 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 - 5.0.0-1.25209.6 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 + 5.0.0-1.25222.12 $(NetVSCode) - enable enable true + $(DefineConstants);VSCODE + @@ -65,4 +66,6 @@ + + diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListener.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListener.cs index 78fe8e5e316..e0f222d77b8 100644 --- a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListener.cs +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListener.cs @@ -1,7 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. +using System.IO; using System.IO.Pipes; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListenerBase.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListenerBase.cs index a3ef36c0349..ba33053a9ea 100644 --- a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListenerBase.cs +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/RazorWorkspaceListenerBase.cs @@ -1,8 +1,13 @@ // 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.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; using Microsoft.AspNetCore.Razor; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor.Utilities; diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/SemanticTokens/CohostSemanticTokensRegistration.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/SemanticTokens/CohostSemanticTokensRegistration.cs new file mode 100644 index 00000000000..9ec610c45a9 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/SemanticTokens/CohostSemanticTokensRegistration.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Composition; +using System.Text.Json; +using Microsoft.AspNetCore.Razor.LanguageServer.Hosting; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +using Microsoft.CodeAnalysis.Razor.SemanticTokens; + +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +[Shared] +[Export(typeof(IDynamicRegistrationProvider))] +[method: ImportingConstructor] +internal sealed class CohostSemanticTokensRegistration(ISemanticTokensLegendService semanticTokensLegendService) : IDynamicRegistrationProvider +{ + private readonly ISemanticTokensLegendService _semanticTokensLegendService = semanticTokensLegendService; + + public ImmutableArray GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext) + { + if (clientCapabilities.TextDocument?.SemanticTokens?.DynamicRegistration == true) + { + var semanticTokensRefreshQueue = requestContext.GetRequiredService(); + var clientCapabilitiesString = JsonSerializer.Serialize(clientCapabilities); + semanticTokensRefreshQueue.Initialize(clientCapabilitiesString); + + return [new Registration() + { + Method = Methods.TextDocumentSemanticTokensName, + RegisterOptions = new SemanticTokensRegistrationOptions() + .EnableSemanticTokens(_semanticTokensLegendService) + }]; + } + + return []; + } +} \ No newline at end of file diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/ClientSettingsReader.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/ClientSettingsReader.cs new file mode 100644 index 00000000000..2edeaeb9ab5 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/ClientSettingsReader.cs @@ -0,0 +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.Composition; +using Microsoft.CodeAnalysis.Razor.Settings; +using Microsoft.CodeAnalysis.Razor.Workspaces.Settings; + +namespace Microsoft.VisualStudioCode.RazorExtension.Services; + +[Shared] +[Export(typeof(IClientSettingsReader))] +internal class ClientSettingsReader : IClientSettingsReader +{ + public ClientSettings GetClientSettings() + { + // TODO: Implement logic to read client settings + return ClientSettings.Default; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileInfoProvider.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileInfoProvider.cs index 769af8e35b7..ab84bd2987a 100644 --- a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileInfoProvider.cs +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileInfoProvider.cs @@ -1,21 +1,31 @@ // 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.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; +using Microsoft.CodeAnalysis.Razor.Workspaces; namespace Microsoft.VisualStudioCode.RazorExtension.Services; -internal sealed partial class LspDynamicFileProvider(IRazorClientLanguageServerManager clientLanguageServerManager) : RazorLspDynamicFileInfoProvider +internal sealed partial class LspDynamicFileProvider(IRazorClientLanguageServerManager clientLanguageServerManager, LanguageServerFeatureOptions languageServerFeatureOptions) : RazorLspDynamicFileInfoProvider { private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; private readonly IRazorClientLanguageServerManager _clientLanguageServerManager = clientLanguageServerManager; + private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions; public override async Task GetDynamicFileInfoAsync(Workspace workspace, ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) { + if (_languageServerFeatureOptions.UseRazorCohostServer) + { + return null; + } + var razorUri = new Uri(filePath); var requestParams = new RazorProvideDynamicFileParams diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileProviderFactory.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileProviderFactory.cs index 05a47cca6f4..da91e709854 100644 --- a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileProviderFactory.cs +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/DynamicFileProviderFactory.cs @@ -4,16 +4,20 @@ using System.Composition; using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; +using Microsoft.CodeAnalysis.Razor.Workspaces; namespace Microsoft.VisualStudioCode.RazorExtension.Services; [ExportRazorLspServiceFactory(typeof(RazorLspDynamicFileInfoProvider)), Shared] -internal sealed class DynamicFileProviderFactory : AbstractRazorLspServiceFactory +[method: ImportingConstructor] +internal sealed class DynamicFileProviderFactory(LanguageServerFeatureOptions featureOptions) : AbstractRazorLspServiceFactory { + private readonly LanguageServerFeatureOptions _featureOptions = featureOptions; + protected override AbstractRazorLspService CreateService(IRazorLspServices lspServices) { var clientLanguageServerManager = lspServices.GetRequiredService(); - return new LspDynamicFileProvider(clientLanguageServerManager); + return new LspDynamicFileProvider(clientLanguageServerManager, _featureOptions); } } diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/InProcServiceFactory.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/InProcServiceFactory.cs new file mode 100644 index 00000000000..793c4bbe86d --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/InProcServiceFactory.cs @@ -0,0 +1,66 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor; +using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Remote.Razor; + +namespace Microsoft.VisualStudioCode.RazorExtension.Services; + +/// +/// Creates Razor brokered services. +/// +/// +/// This class holds instances in a static +/// field. This should work fine in tests, since brokered service factories are intended to be stateless. +/// However, if a factory is introduced that maintains state, this class will need to be revisited to +/// avoid holding onto state across tests. +/// +internal static class InProcServiceFactory +{ + private static readonly Dictionary s_factoryMap = BuildFactoryMap(); + + private static Dictionary BuildFactoryMap() + { + var result = new Dictionary(); + + foreach (var type in typeof(RazorBrokeredServiceBase.FactoryBase<>).Assembly.GetTypes()) + { + if (!type.IsAbstract && + typeof(IInProcServiceFactory).IsAssignableFrom(type)) + { + Assumes.True(typeof(RazorBrokeredServiceBase.FactoryBase<>) == type.BaseType!.GetGenericTypeDefinition()); + + var genericType = type.BaseType.GetGenericArguments().Single(); + + // ServiceHub requires a parameterless constructor, so we can safely rely on it existing too + var factory = (IInProcServiceFactory)Activator.CreateInstance(type).AssumeNotNull(); + result.Add(genericType, factory); + } + } + + return result; + } + + public static async Task CreateServiceAsync( + VSCodeBrokeredServiceInterceptor brokeredServiceInterceptor, ILoggerFactory loggerFactory) + where TService : class + { + Assumes.True(s_factoryMap.TryGetValue(typeof(TService), out var factory)); + + var brokeredServiceData = new RazorBrokeredServiceData(ExportProvider: null, loggerFactory, brokeredServiceInterceptor); + var hostProvidedServices = new HostProvidedServices(brokeredServiceData); + + return (TService)await factory.CreateInProcAsync(hostProvidedServices).ConfigureAwait(false); + } + + private class HostProvidedServices(RazorBrokeredServiceData brokeredServiceData) : IServiceProvider + { + public object? GetService(Type serviceType) + => serviceType == typeof(RazorBrokeredServiceData) ? brokeredServiceData : null; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LoggerFactory.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LoggerFactory.cs new file mode 100644 index 00000000000..dede048009f --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LoggerFactory.cs @@ -0,0 +1,15 @@ +// 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.Logging; + +namespace Microsoft.AspNetCore.Razor.LanguageServer; + +[Shared] +[Export(typeof(ILoggerFactory))] +[method: ImportingConstructor] +internal sealed class LoggerFactory(ILoggerProvider provider) + : AbstractLoggerFactory([provider]) +{ +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LoggerProvider.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LoggerProvider.cs new file mode 100644 index 00000000000..b8ad83a9a4d --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LoggerProvider.cs @@ -0,0 +1,20 @@ +// 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.Logging; + +namespace Microsoft.AspNetCore.Razor.LanguageServer; + +[Shared] +[Export(typeof(ILoggerProvider))] +[method: ImportingConstructor] +internal class LoggerProvider(RazorClientServerManagerProvider razorClientServerManagerProvider) : ILoggerProvider +{ + private readonly RazorClientServerManagerProvider _razorClientServerManagerProvider = razorClientServerManagerProvider; + + public ILogger CreateLogger(string categoryName) + { + return new LspLogger(categoryName, _razorClientServerManagerProvider); + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LspDynamicFileProvider.LspTextChangesTextLoader.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LspDynamicFileProvider.LspTextChangesTextLoader.cs index 718def982a7..9142f62f9dc 100644 --- a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LspDynamicFileProvider.LspTextChangesTextLoader.cs +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LspDynamicFileProvider.LspTextChangesTextLoader.cs @@ -1,8 +1,13 @@ // 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.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.Razor.Protocol; diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LspLogger.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LspLogger.cs new file mode 100644 index 00000000000..1e246da17f9 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/LspLogger.cs @@ -0,0 +1,57 @@ +// 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.Threading; +using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.VisualStudio.Threading; + +namespace Microsoft.AspNetCore.Razor.LanguageServer; + +/// +/// ILogger implementation that logs via the razor/log LSP method +/// +/// +/// The handler for this custom log message is implemented in the C# extension and is responsible for writing the message to the output window. +/// +internal class LspLogger(string categoryName, RazorClientServerManagerProvider razorClientServerManagerProvider) : ILogger +{ + private readonly string _categoryName = categoryName; + private readonly RazorClientServerManagerProvider _razorClientServerManagerProvider = razorClientServerManagerProvider; + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, string message, Exception? exception) + { + if (_razorClientServerManagerProvider.ClientLanguageServerManager is not { } clientLanguageServerManager) + { + return; + } + + if (!IsEnabled(logLevel)) + { + return; + } + + var messageType = logLevel switch + { + LogLevel.Critical => MessageType.Error, + LogLevel.Error => MessageType.Error, + LogLevel.Warning => MessageType.Warning, + LogLevel.Information => MessageType.Info, + LogLevel.Debug => MessageType.Log, + LogLevel.Trace => MessageType.Log, + _ => throw new NotImplementedException(), + }; + + var formattedMessage = LogMessageFormatter.FormatMessage(message, _categoryName, exception, includeTimeStamp: false); + + var @params = new LogMessageParams + { + MessageType = messageType, + Message = formattedMessage, + }; + + clientLanguageServerManager.SendNotificationAsync("razor/log", @params, CancellationToken.None).Forget(); + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/RazorClientServerManagerProvider.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/RazorClientServerManagerProvider.cs new file mode 100644 index 00000000000..0e140911796 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/RazorClientServerManagerProvider.cs @@ -0,0 +1,28 @@ +// 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 System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +using Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +namespace Microsoft.AspNetCore.Razor.LanguageServer; + +[Shared] +[Export(typeof(IRazorCohostStartupService))] +[Export(typeof(RazorClientServerManagerProvider))] +[method: ImportingConstructor] +internal class RazorClientServerManagerProvider() : IRazorCohostStartupService +{ + private IRazorClientLanguageServerManager? _razorClientLanguageServerManager; + + public IRazorClientLanguageServerManager? ClientLanguageServerManager => _razorClientLanguageServerManager; + + public Task StartupAsync(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext, CancellationToken cancellationToken) + { + _razorClientLanguageServerManager = requestContext.GetRequiredService(); + return Task.CompletedTask; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeBrokeredServiceInterceptor.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeBrokeredServiceInterceptor.cs new file mode 100644 index 00000000000..2c317fe9031 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeBrokeredServiceInterceptor.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 System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Remote.Razor; + +namespace Microsoft.VisualStudioCode.RazorExtension.Services; + +internal class VSCodeBrokeredServiceInterceptor : IRazorBrokeredServiceInterceptor +{ + public ValueTask RunServiceAsync(Func implementation, CancellationToken cancellationToken) + => implementation(cancellationToken); + + public ValueTask RunServiceAsync(RazorPinnedSolutionInfoWrapper solutionInfo, Func> implementation, CancellationToken cancellationToken) + => implementation(solutionInfo.Solution.AssumeNotNull()); +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeLanguageServerFeatureOptions.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeLanguageServerFeatureOptions.cs new file mode 100644 index 00000000000..e2411846e49 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeLanguageServerFeatureOptions.cs @@ -0,0 +1,83 @@ +// 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 System.Text.Json.Nodes; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Utilities; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +using Microsoft.CodeAnalysis.Razor.Workspaces; +using Microsoft.NET.Sdk.Razor.SourceGenerators; +using Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +namespace Microsoft.VisualStudioCode.RazorExtension.Services; + +[Shared] +[Export(typeof(LanguageServerFeatureOptions))] +internal class VSCodeLanguageServerFeatureOptions : LanguageServerFeatureOptions, IRazorCohostStartupService +{ + private bool _useRazorCohostServer = false; + private bool _useNewFormattingEngine = true; + private bool _forceRuntimeCodeGeneration = false; + + // Options that are set to their defaults + public override bool SupportsFileManipulation => true; + public override bool SingleServerSupport => false; + public override bool UsePreciseSemanticTokenRanges => false; + public override bool ShowAllCSharpCodeActions => false; + public override bool ReturnCodeActionAndRenamePathsWithPrefixedSlash => PlatformInformation.IsWindows; + public override bool IncludeProjectKeyInGeneratedFilePath => false; + public override bool DoNotInitializeMiscFilesProjectFromWorkspace => false; + + // Options that differ from the default + public override string CSharpVirtualDocumentSuffix => "__virtual.cs"; + public override string HtmlVirtualDocumentSuffix => "__virtual.html"; + public override bool UpdateBuffersForClosedDocuments => true; + public override bool DelegateToCSharpOnDiagnosticPublish => true; + public override bool SupportsSoftSelectionInCompletion => false; + public override bool UseVsCodeCompletionTriggerCharacters => true; + + // User configurable options + public override bool UseRazorCohostServer => _useRazorCohostServer; + public override bool ForceRuntimeCodeGeneration => _forceRuntimeCodeGeneration; + public override bool UseNewFormattingEngine => _useNewFormattingEngine; + + public async Task StartupAsync(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext, CancellationToken cancellationToken) + { + var razorClientLanguageServerManager = requestContext.GetRequiredService(); + + // Attempt to get configurations from the client. If this throws we'll get NFW reports. + var configurationParams = new ConfigurationParams() + { + Items = [ + // Roslyn's typescript config handler will convert underscores to camelcase, ie 'razor.languageServer.cohostingEnabled' + new ConfigurationItem { Section = "razor.language_server.cohosting_enabled" }, + new ConfigurationItem { Section = "razor.language_server.use_new_formatting_engine" }, + new ConfigurationItem { Section = "razor.language_server.force_runtime_code_generation" }, + + ] + }; + var options = await razorClientLanguageServerManager.SendRequestAsync( + Methods.WorkspaceConfigurationName, + configurationParams, + cancellationToken).ConfigureAwait(false); + + _useRazorCohostServer = GetBooleanOptionValue(options[0], _useRazorCohostServer); + _useNewFormattingEngine = GetBooleanOptionValue(options[1], _useNewFormattingEngine); + _forceRuntimeCodeGeneration = GetBooleanOptionValue(options[2], _forceRuntimeCodeGeneration); + + RazorCohostingOptions.UseRazorCohostServer = _useRazorCohostServer; + } + + private static bool GetBooleanOptionValue(JsonNode? jsonNode, bool defaultValue) + { + if (jsonNode is null) + { + return defaultValue; + } + + return jsonNode.ToString() == "true"; + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeRemoteServiceInvoker.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeRemoteServiceInvoker.cs new file mode 100644 index 00000000000..676cd4b952f --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeRemoteServiceInvoker.cs @@ -0,0 +1,95 @@ +// 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.Collections.Generic; +using System.Composition; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.Remote; +using Microsoft.VisualStudio.Composition; + +namespace Microsoft.VisualStudioCode.RazorExtension.Services; + +[Shared] +[Export(typeof(IRemoteServiceInvoker))] +[method: ImportingConstructor] +internal class VSCodeRemoteServiceInvoker( + ILoggerFactory loggerFactory) : IRemoteServiceInvoker, IDisposable +{ + private readonly ILoggerFactory _loggerFactory = loggerFactory; + private readonly Dictionary _services = []; + private readonly Lock _serviceLock = new(); + private readonly VSCodeBrokeredServiceInterceptor _serviceInterceptor = new(); + + public async ValueTask TryInvokeAsync( + Solution solution, + Func> invocation, + CancellationToken cancellationToken, + [CallerFilePath] string? callerFilePath = null, + [CallerMemberName] string? callerMemberName = null) where TService : class + { + var service = await GetOrCreateServiceAsync().ConfigureAwait(false); + + if (service == null) + { + // Service not available + return default; + } + + // Get solution info with direct reference stored + var solutionInfo = new RazorPinnedSolutionInfoWrapper(checksum: default, solution: solution); + + // Invoke the function with the service and solution info + return await invocation(service, solutionInfo, cancellationToken).ConfigureAwait(false); + } + + private async Task GetOrCreateServiceAsync() where TService : class + { + lock (_serviceLock) + { + if (_services.TryGetValue(typeof(TService), out var existingService)) + { + return (TService)existingService; + } + } + + // Create the service using the InProcServiceFactory + try + { + var service = await InProcServiceFactory.CreateServiceAsync(_serviceInterceptor, _loggerFactory).ConfigureAwait(false); + + lock (_serviceLock) + { + _services[typeof(TService)] = service; + } + + return service; + } + catch (Exception) + { + // If service creation fails, return null + return null; + } + } + + public void Dispose() + { + lock (_serviceLock) + { + foreach (var service in _services.Values) + { + if (service is IDisposable d) + { + d.Dispose(); + } + } + + _services.Clear(); + } + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeRemoteServicesInitializer.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeRemoteServicesInitializer.cs new file mode 100644 index 00000000000..13d901f8c61 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeRemoteServicesInitializer.cs @@ -0,0 +1,54 @@ +// 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 System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.Remote; +using Microsoft.CodeAnalysis.Razor.SemanticTokens; +using Microsoft.CodeAnalysis.Razor.Workspaces; +using Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +namespace Microsoft.VisualStudioCode.RazorExtension.Services; + +[Shared] +[Export(typeof(IRazorCohostStartupService))] +[method: ImportingConstructor] +internal class VSCodeRemoteServicesInitializer( + LanguageServerFeatureOptions featureOptions, + ISemanticTokensLegendService semanticTokensLegendService, + ILoggerFactory loggerFactory) : IRazorCohostStartupService +{ + private readonly LanguageServerFeatureOptions _featureOptions = featureOptions; + private readonly ISemanticTokensLegendService _semanticTokensLegendService = semanticTokensLegendService; + private readonly ILoggerFactory _loggerFactory = loggerFactory; + + public async Task StartupAsync(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext, CancellationToken cancellationToken) + { + // Normal remote service invoker logic requires a solution, but we don't have one here. Fortunately we don't need one, and since + // we know this is VS Code specific, its all just smoke and mirrors anyway. We can avoid the smoke :) + var serviceInterceptor = new VSCodeBrokeredServiceInterceptor(); + var service = await InProcServiceFactory.CreateServiceAsync(serviceInterceptor, _loggerFactory).ConfigureAwait(false); + + await service.InitializeAsync(new RemoteClientInitializationOptions + { + UseRazorCohostServer = _featureOptions.UseRazorCohostServer, + UsePreciseSemanticTokenRanges = _featureOptions.UsePreciseSemanticTokenRanges, + HtmlVirtualDocumentSuffix = _featureOptions.HtmlVirtualDocumentSuffix, + ReturnCodeActionAndRenamePathsWithPrefixedSlash = _featureOptions.ReturnCodeActionAndRenamePathsWithPrefixedSlash, + SupportsFileManipulation = _featureOptions.SupportsFileManipulation, + ShowAllCSharpCodeActions = _featureOptions.ShowAllCSharpCodeActions, + SupportsSoftSelectionInCompletion = _featureOptions.SupportsSoftSelectionInCompletion, + UseVsCodeCompletionTriggerCharacters = _featureOptions.UseVsCodeCompletionTriggerCharacters, + }, cancellationToken).ConfigureAwait(false); + + await service.InitializeLSPAsync(new RemoteClientLSPInitializationOptions + { + ClientCapabilities = clientCapabilities, + TokenTypes = _semanticTokensLegendService.TokenTypes.All, + TokenModifiers = _semanticTokensLegendService.TokenModifiers.All, + }, cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeTelemetryReporter.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeTelemetryReporter.cs new file mode 100644 index 00000000000..e5ae9ff8bfd --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/VSCodeTelemetryReporter.cs @@ -0,0 +1,61 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.Razor.Telemetry; + +namespace Microsoft.VisualStudioCode.RazorExtension.Services; + +// TODO: + +[Shared] +[Export(typeof(ITelemetryReporter))] +internal class VSCodeTelemetryReporter : ITelemetryReporter +{ + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport) + => TelemetryScope.Null; + + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property) + => TelemetryScope.Null; + + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2) + => TelemetryScope.Null; + + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, Property property1, Property property2, Property property3) + => TelemetryScope.Null; + + public TelemetryScope BeginBlock(string name, Severity severity, TimeSpan minTimeToReport, params ReadOnlySpan properties) + => TelemetryScope.Null; + + public void ReportEvent(string name, Severity severity) + { + } + + public void ReportEvent(string name, Severity severity, Property property) + { + } + + public void ReportEvent(string name, Severity severity, Property property1, Property property2) + { + } + + public void ReportEvent(string name, Severity severity, Property property1, Property property2, Property property3) + { + } + + public void ReportEvent(string name, Severity severity, params ReadOnlySpan properties) + { + } + + public void ReportFault(Exception exception, string? message, params object?[] @params) + { + } + + public TelemetryScope TrackLspRequest(string lspMethodName, string lspServerName, TimeSpan minTimeToReport, Guid correlationId) + => TelemetryScope.Null; + + public void ReportRequestTiming(string name, string? language, TimeSpan queuedDuration, TimeSpan requestDuration, TelemetryResult result) + { + } +} diff --git a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/WorkspaceService.cs b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/WorkspaceService.cs index 420d6a7aa41..2b1ebb8941d 100644 --- a/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/WorkspaceService.cs +++ b/src/Razor/src/Microsoft.VisualStudioCode.RazorExtension/Services/WorkspaceService.cs @@ -1,26 +1,37 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Composition; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; +using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.Extensions.Logging; namespace Microsoft.VisualStudioCode.RazorExtension.Services; [ExportRazorStatelessLspService(typeof(RazorWorkspaceService)), Shared] [method: ImportingConstructor] -internal sealed class WorkspaceService(ILoggerFactory loggerFactory) : RazorWorkspaceService +internal sealed class WorkspaceService(ILoggerFactory loggerFactory, LanguageServerFeatureOptions languageServerFeatureOptions) : RazorWorkspaceService { private readonly ILoggerFactory _loggerFactory = loggerFactory; private readonly ILogger _logger = loggerFactory.CreateLogger(); private readonly Lock _initializeLock = new(); + private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions; + private RazorWorkspaceListener? _razorWorkspaceListener; private HashSet? _projectIdWithDynamicFiles = []; public override void Initialize(Workspace workspace, string pipeName) { + if (_languageServerFeatureOptions.UseRazorCohostServer) + { + // Cohost server doesn't need to initialize the workspace listener. + return; + } + HashSet projectsToInitialize; lock (_initializeLock) { @@ -48,6 +59,12 @@ public override void Initialize(Workspace workspace, string pipeName) public override void NotifyDynamicFile(ProjectId projectId) { + if (_languageServerFeatureOptions.UseRazorCohostServer) + { + // Cohost server doesn't need to initialize the workspace listener. + return; + } + if (_razorWorkspaceListener is null) { lock (_initializeLock) diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTest.cs index cd80fb0f698..e025cd3b34d 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostEndpointTest.cs @@ -100,7 +100,7 @@ public void RegistrationsProvideFilter() foreach (var endpoint in providers) { - if (endpoint is CohostSemanticTokensRangeEndpoint) + if (endpoint is CohostSemanticTokensRegistration) { // We can't currently test this, as the GetRegistrations method calls requestContext.GetRequiredService // and we can't create a request context ourselves 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 219135e1c21..3b2861efa10 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 @@ -116,6 +116,10 @@ protected override async Task InitializeAsync() UpdateClientLSPInitializationOptions(c => c); _filePathService = new RemoteFilePathService(FeatureOptions); + + // Force initialization and creation of the remote workspace. It will be filled in later. + await RemoteWorkspaceAccessor.TestAccessor.InitializeRemoteExportProviderBuilderAsync(Path.GetTempPath(), DisposalToken); + _ = RemoteWorkspaceAccessor.GetWorkspace(); } private protected void UpdateClientInitializationOptions(Func mutation) 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 ba51cfa1cce..731580a3bd4 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 @@ -134,7 +134,7 @@ private async Task VerifySemanticTokensAsync( var clientSettingsManager = new ClientSettingsManager([], null, null); clientSettingsManager.Update(ClientAdvancedSettings.Default with { ColorBackground = colorBackground }); - var endpoint = new CohostSemanticTokensRangeEndpoint(RemoteServiceInvoker, clientSettingsManager, legend, NoOpTelemetryReporter.Instance); + var endpoint = new CohostSemanticTokensRangeEndpoint(RemoteServiceInvoker, clientSettingsManager, NoOpTelemetryReporter.Instance); var span = new LinePositionSpan(new(0, 0), new(sourceText.Lines.Count, 0));