Initial cohost server implementation (#9674)

First part of https://github.com/dotnet/razor/issues/9519
Razor side of https://github.com/dotnet/roslyn/pull/70819

This PR consumes the new Cohost server from Roslyn, and when enabled,
moves DocumentColor requests from being handled by our existing Razor
Language Server, to instead be handled by the new server. There is
currently no sharing of project system data or anything like that yet. A
few TODOs for follow ups, ~can't be merged, and indeed won't build,
until a Roslyn package is available etc. but~ it's reviewable while we
keep working, and more important its shippable in its "off" state.

~Ignore the last commit, obviously won't be merged :)~
This commit is contained in:
David Wengier 2024-01-04 15:43:47 +11:00 коммит произвёл GitHub
Родитель 376aca3029 463f86989b
Коммит 13d3145dc9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
35 изменённых файлов: 481 добавлений и 177 удалений

Просмотреть файл

@ -65,6 +65,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{4CAC99E0-6ECE-4264-96C3-AF4EEE7BC9D1}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
NuGet.config = NuGet.config
SpellingExclusions.dic = SpellingExclusions.dic
eng\Versions.props = eng\Versions.props
EndProjectSection

Просмотреть файл

@ -10,78 +10,78 @@
<Sha>2a008ae4f42c0db384db5a4864752b2ff52d720b</Sha>
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
<SourceBuild RepoName="roslyn" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.CommonLanguageServerProtocol.Framework" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CommonLanguageServerProtocol.Framework" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.ExternalAccess.Razor" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.ExternalAccess.Razor" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Common" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.Common" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.CSharp" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.EditorFeatures" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.EditorFeatures" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Features" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Features" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Common" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Common" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Text" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Text" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Wpf" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Wpf" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Remote.ServiceHub" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.Remote.ServiceHub" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.VisualStudio.LanguageServices" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.VisualStudio.LanguageServices" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Test.Utilities" Version="4.9.0-1.23530.5">
<Dependency Name="Microsoft.CodeAnalysis.Test.Utilities" Version="4.9.0-3.23613.4">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>5a41731d6717929d437137c1d0373135b5af88a0</Sha>
<Sha>93677976e310e7f910f289e962566485cbabf8f3</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>

Просмотреть файл

@ -52,24 +52,24 @@
<MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>6.0.2-servicing.22064.6</MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>
<MicrosoftNETCorePlatformsPackageVersion>6.0.1</MicrosoftNETCorePlatformsPackageVersion>
<MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>9.0.0-alpha.1.23627.2</MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>
<MicrosoftNetCompilersToolsetPackageVersion>4.9.0-1.23530.5</MicrosoftNetCompilersToolsetPackageVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>4.9.0-1.23530.5</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
<MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>
<MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>
<MicrosoftCodeAnalysisTestUtilitiesPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisTestUtilitiesPackageVersion>
<MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
<MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>4.9.0-1.23530.5</MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>
<MicrosoftVisualStudioLanguageServicesPackageVersion>4.9.0-1.23530.5</MicrosoftVisualStudioLanguageServicesPackageVersion>
<MicrosoftNetCompilersToolsetPackageVersion>4.9.0-3.23613.4</MicrosoftNetCompilersToolsetPackageVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>4.9.0-3.23613.4</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
<MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>
<MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>
<MicrosoftCodeAnalysisTestUtilitiesPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisTestUtilitiesPackageVersion>
<MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
<MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>4.9.0-3.23613.4</MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>
<MicrosoftVisualStudioLanguageServicesPackageVersion>4.9.0-3.23613.4</MicrosoftVisualStudioLanguageServicesPackageVersion>
<MicrosoftDotNetXliffTasksPackageVersion>1.0.0-beta.23475.1</MicrosoftDotNetXliffTasksPackageVersion>
<!--
Exception - Microsoft.Extensions.ObjectPool and System.Collections.Immutable packages are not updated by automation,

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Composition;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -11,7 +12,9 @@ using Microsoft.CodeAnalysis.Text;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Common;
internal class DefaultRemoteTextLoaderFactory : RemoteTextLoaderFactory
[Export(typeof(RemoteTextLoaderFactory)), Shared]
[method: ImportingConstructor]
internal class DefaultRemoteTextLoaderFactory() : RemoteTextLoaderFactory
{
public override TextLoader Create(string filePath)
{

Просмотреть файл

@ -23,6 +23,7 @@ internal class ConfigurableLanguageServerFeatureOptions : LanguageServerFeatureO
private readonly bool? _updateBuffersForClosedDocuments;
private readonly bool? _includeProjectKeyInGeneratedFilePath;
private readonly bool? _monitorWorkspaceFolderForConfigurationFiles;
private readonly bool? _useRazorCohostServer;
public override bool SupportsFileManipulation => _supportsFileManipulation ?? _defaults.SupportsFileManipulation;
public override string ProjectConfigurationFileName => _projectConfigurationFileName ?? _defaults.ProjectConfigurationFileName;
@ -37,6 +38,7 @@ internal class ConfigurableLanguageServerFeatureOptions : LanguageServerFeatureO
public override bool UpdateBuffersForClosedDocuments => _updateBuffersForClosedDocuments ?? _defaults.UpdateBuffersForClosedDocuments;
public override bool IncludeProjectKeyInGeneratedFilePath => _includeProjectKeyInGeneratedFilePath ?? _defaults.IncludeProjectKeyInGeneratedFilePath;
public override bool MonitorWorkspaceFolderForConfigurationFiles => _monitorWorkspaceFolderForConfigurationFiles ?? _defaults.MonitorWorkspaceFolderForConfigurationFiles;
public override bool UseRazorCohostServer => _useRazorCohostServer ?? _defaults.UseRazorCohostServer;
public ConfigurableLanguageServerFeatureOptions(string[] args)
{
@ -60,6 +62,7 @@ internal class ConfigurableLanguageServerFeatureOptions : LanguageServerFeatureO
TryProcessBoolOption(nameof(UpdateBuffersForClosedDocuments), ref _updateBuffersForClosedDocuments, option, args, i);
TryProcessBoolOption(nameof(IncludeProjectKeyInGeneratedFilePath), ref _includeProjectKeyInGeneratedFilePath, option, args, i);
TryProcessBoolOption(nameof(MonitorWorkspaceFolderForConfigurationFiles), ref _monitorWorkspaceFolderForConfigurationFiles, option, args, i);
TryProcessBoolOption(nameof(UseRazorCohostServer), ref _useRazorCohostServer, option, args, i);
}
}

Просмотреть файл

@ -40,4 +40,6 @@ internal class DefaultLanguageServerFeatureOptions : LanguageServerFeatureOption
public override bool UsePreciseSemanticTokenRanges => false;
public override bool MonitorWorkspaceFolderForConfigurationFiles => true;
public override bool UseRazorCohostServer => false;
}

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -12,52 +11,19 @@ using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.DocumentColor;
[LanguageServerEndpoint(Methods.TextDocumentDocumentColorName)]
internal sealed class DocumentColorEndpoint : IRazorRequestHandler<DocumentColorParams, ColorInformation[]>, ICapabilitiesProvider
internal sealed class DocumentColorEndpoint(IDocumentColorService documentColorService, IClientConnection clientConnection) : IRazorRequestHandler<DocumentColorParams, ColorInformation[]>, ICapabilitiesProvider
{
private readonly IClientConnection _clientConnection;
public DocumentColorEndpoint(IClientConnection clientConnection)
{
_clientConnection = clientConnection ?? throw new ArgumentNullException(nameof(clientConnection));
}
private readonly IDocumentColorService _documentColorService = documentColorService ?? throw new ArgumentNullException(nameof(documentColorService));
private readonly IClientConnection _clientConnection = clientConnection ?? throw new ArgumentNullException(nameof(clientConnection));
public bool MutatesSolutionState => false;
public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
{
serverCapabilities.DocumentColorProvider = new DocumentColorOptions();
}
=> _documentColorService.ApplyCapabilities(serverCapabilities, clientCapabilities);
public TextDocumentIdentifier GetTextDocumentIdentifier(DocumentColorParams request)
=> request.TextDocument;
public async Task<ColorInformation[]> HandleRequestAsync(DocumentColorParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
{
// Workaround for Web Tools bug https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1743689 where they sometimes
// send requests for filenames that are stale, possibly due to adornment taggers being cached incorrectly (or caching
// filenames incorrectly)
if (requestContext.DocumentContext is null)
{
return Array.Empty<ColorInformation>();
}
var delegatedRequest = new DelegatedDocumentColorParams()
{
HostDocumentVersion = requestContext.GetRequiredDocumentContext().Version,
TextDocument = request.TextDocument
};
var documentColors = await _clientConnection.SendRequestAsync<DelegatedDocumentColorParams, ColorInformation[]>(
CustomMessageNames.RazorProvideHtmlDocumentColorEndpoint,
delegatedRequest,
cancellationToken).ConfigureAwait(false);
if (documentColors is null)
{
return Array.Empty<ColorInformation>();
}
// HTML and Razor documents have identical mapping locations. Because of this we can return the result as-is.
return documentColors;
}
public Task<ColorInformation[]> HandleRequestAsync(DocumentColorParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
=> _documentColorService.GetColorInformationAsync(_clientConnection, request, requestContext.DocumentContext, cancellationToken);
}

Просмотреть файл

@ -0,0 +1,50 @@
// 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.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.DocumentColor;
[Export(typeof(IDocumentColorService)), Shared]
[method: ImportingConstructor]
internal sealed class DocumentColorService() : IDocumentColorService
{
public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
{
serverCapabilities.DocumentColorProvider = new DocumentColorOptions();
}
public async Task<ColorInformation[]> GetColorInformationAsync(IClientConnection clientConnection, DocumentColorParams request, VersionedDocumentContext? documentContext, CancellationToken cancellationToken)
{
// Workaround for Web Tools bug https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1743689 where they sometimes
// send requests for filenames that are stale, possibly due to adornment taggers being cached incorrectly (or caching
// filenames incorrectly)
if (documentContext is null)
{
return [];
}
var delegatedRequest = new DelegatedDocumentColorParams()
{
HostDocumentVersion = documentContext.Version,
TextDocument = request.TextDocument
};
var documentColors = await clientConnection.SendRequestAsync<DelegatedDocumentColorParams, ColorInformation[]>(
CustomMessageNames.RazorProvideHtmlDocumentColorEndpoint,
delegatedRequest,
cancellationToken).ConfigureAwait(false);
if (documentColors is null)
{
return [];
}
// HTML and Razor documents have identical mapping locations. Because of this we can return the result as-is.
return documentColors;
}
}

Просмотреть файл

@ -0,0 +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.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.DocumentColor;
internal interface IDocumentColorService : ICapabilitiesProvider
{
Task<ColorInformation[]> GetColorInformationAsync(IClientConnection clientConnection, DocumentColorParams request, VersionedDocumentContext? documentContext, CancellationToken cancellationToken);
}

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Composition;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
@ -16,6 +17,8 @@ using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer;
[Export(typeof(IDocumentContextFactory)), Shared]
[method: ImportingConstructor]
internal sealed class DocumentContextFactory(
ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor,
ISnapshotResolver snapshotResolver,

Просмотреть файл

@ -3,13 +3,19 @@
using System;
using System.Collections.Generic;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.LanguageServer;
internal sealed class DocumentVersionCache : IDocumentVersionCache, IProjectSnapshotChangeTrigger
// TODO: This has to be Shared for MEF to work, but this service was written assuming its lifetime was that of the language
// server, so it doesn't clean up after itself well. In the long run, this hopefully won't matter, as we can remove it
// but leaving this note here because you never know.
[Export(typeof(IDocumentVersionCache)), Shared]
[method: ImportingConstructor]
internal sealed class DocumentVersionCache() : IDocumentVersionCache, IProjectSnapshotChangeTrigger
{
internal const int MaxDocumentTrackingCount = 20;

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -23,60 +24,23 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
internal class DefaultRazorProjectService : RazorProjectService
[Export(typeof(RazorProjectService)), Shared]
[method: ImportingConstructor]
internal class DefaultRazorProjectService(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
RemoteTextLoaderFactory remoteTextLoaderFactory,
ISnapshotResolver snapshotResolver,
IDocumentVersionCache documentVersionCache,
ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor,
IRazorLoggerFactory loggerFactory)
: RazorProjectService
{
private readonly ProjectSnapshotManagerAccessor _projectSnapshotManagerAccessor;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly RemoteTextLoaderFactory _remoteTextLoaderFactory;
private readonly ISnapshotResolver _snapshotResolver;
private readonly IDocumentVersionCache _documentVersionCache;
private readonly ILogger _logger;
public DefaultRazorProjectService(
ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
RemoteTextLoaderFactory remoteTextLoaderFactory,
ISnapshotResolver snapshotResolver,
IDocumentVersionCache documentVersionCache,
ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor,
IRazorLoggerFactory loggerFactory)
{
if (projectSnapshotManagerDispatcher is null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
}
if (remoteTextLoaderFactory is null)
{
throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
}
if (snapshotResolver is null)
{
throw new ArgumentNullException(nameof(snapshotResolver));
}
if (documentVersionCache is null)
{
throw new ArgumentNullException(nameof(documentVersionCache));
}
if (projectSnapshotManagerAccessor is null)
{
throw new ArgumentNullException(nameof(projectSnapshotManagerAccessor));
}
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
_remoteTextLoaderFactory = remoteTextLoaderFactory;
_snapshotResolver = snapshotResolver;
_documentVersionCache = documentVersionCache;
_projectSnapshotManagerAccessor = projectSnapshotManagerAccessor;
_logger = loggerFactory.CreateLogger<DefaultRazorProjectService>();
}
private readonly ProjectSnapshotManagerAccessor _projectSnapshotManagerAccessor = projectSnapshotManagerAccessor ?? throw new ArgumentNullException(nameof(projectSnapshotManagerAccessor));
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher ?? throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
private readonly RemoteTextLoaderFactory _remoteTextLoaderFactory = remoteTextLoaderFactory ?? throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
private readonly ISnapshotResolver _snapshotResolver = snapshotResolver ?? throw new ArgumentNullException(nameof(snapshotResolver));
private readonly IDocumentVersionCache _documentVersionCache = documentVersionCache ?? throw new ArgumentNullException(nameof(documentVersionCache));
private readonly ILogger _logger = loggerFactory.CreateLogger<DefaultRazorProjectService>();
public override void AddDocument(string filePath)
{

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
@ -15,7 +16,8 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
internal class SnapshotResolver : ISnapshotResolver
[Export(typeof(ISnapshotResolver)), Shared]
internal sealed class SnapshotResolver : ISnapshotResolver
{
private readonly ProjectSnapshotManagerAccessor _projectSnapshotManagerAccessor;
private readonly ILogger _logger;
@ -23,10 +25,11 @@ internal class SnapshotResolver : ISnapshotResolver
// Internal for testing
internal readonly HostProject MiscellaneousHostProject;
[ImportingConstructor]
public SnapshotResolver(ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor, IRazorLoggerFactory loggerFactory)
{
_projectSnapshotManagerAccessor = projectSnapshotManagerAccessor ?? throw new ArgumentNullException(nameof(projectSnapshotManagerAccessor));
_logger = loggerFactory.CreateLogger<SnapshotResolver>() ?? throw new ArgumentNullException(nameof(loggerFactory));
_logger = loggerFactory.CreateLogger<SnapshotResolver>();
var miscellaneousProjectPath = Path.Combine(TempDirectory.Instance.DirectoryPath, "__MISC_RAZOR_PROJECT__");
var normalizedPath = FilePathNormalizer.Normalize(miscellaneousProjectPath);

Просмотреть файл

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading;
@ -22,21 +23,17 @@ using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer;
internal sealed class RazorDocumentMappingService : IRazorDocumentMappingService
{
private readonly FilePathService _documentFilePathService;
private readonly IDocumentContextFactory _documentContextFactory;
private readonly ILogger _logger;
public RazorDocumentMappingService(
[Export(typeof(IRazorDocumentMappingService)), Shared]
[method: ImportingConstructor]
internal sealed class RazorDocumentMappingService(
FilePathService filePathService,
IDocumentContextFactory documentContextFactory,
IRazorLoggerFactory loggerFactory)
{
_documentFilePathService = filePathService ?? throw new ArgumentNullException(nameof(filePathService));
_documentContextFactory = documentContextFactory ?? throw new ArgumentNullException(nameof(documentContextFactory));
_logger = loggerFactory?.CreateLogger<RazorDocumentMappingService>() ?? throw new ArgumentNullException(nameof(loggerFactory));
}
: IRazorDocumentMappingService
{
private readonly FilePathService _documentFilePathService = filePathService ?? throw new ArgumentNullException(nameof(filePathService));
private readonly IDocumentContextFactory _documentContextFactory = documentContextFactory ?? throw new ArgumentNullException(nameof(documentContextFactory));
private readonly ILogger _logger = loggerFactory.CreateLogger<RazorDocumentMappingService>();
public TextEdit[] GetHostDocumentEdits(IRazorGeneratedDocument generatedDocument, TextEdit[] generatedDocumentEdits)
{

Просмотреть файл

@ -157,13 +157,13 @@ internal partial class RazorLanguageServer : AbstractLanguageServer<RazorRequest
// Defaults: For when the caller hasn't provided them through the `configure` action.
services.TryAddSingleton<HostServicesProvider, DefaultHostServicesProvider>();
AddHandlers(services);
AddHandlers(services, featureOptions);
var lspServices = new LspServices(services);
return lspServices;
static void AddHandlers(IServiceCollection services)
static void AddHandlers(IServiceCollection services, LanguageServerFeatureOptions featureOptions)
{
// Not calling AddHandler because we want to register this endpoint as an IOnInitialized too
services.AddSingleton<RazorConfigurationEndpoint>();
@ -182,7 +182,13 @@ internal partial class RazorLanguageServer : AbstractLanguageServer<RazorRequest
services.AddHandler<WrapWithTagEndpoint>();
services.AddHandler<RazorBreakpointSpanEndpoint>();
services.AddHandler<RazorProximityExpressionsEndpoint>();
services.AddHandlerWithCapabilities<DocumentColorEndpoint>();
if (!featureOptions.UseRazorCohostServer)
{
services.AddHandlerWithCapabilities<DocumentColorEndpoint>();
services.AddSingleton<IDocumentColorService, DocumentColorService>();
}
services.AddHandler<ColorPresentationEndpoint>();
services.AddHandlerWithCapabilities<FoldingRangeEndpoint>();
services.AddHandlerWithCapabilities<ValidateBreakpointRangeEndpoint>();

Просмотреть файл

@ -3,14 +3,27 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Utilities;
internal static class FilePathNormalizer
{
private static Lazy<IEqualityComparer<string>> _lazyComparer = new Lazy<IEqualityComparer<string>>(() => new FilePathNormalizingComparer());
public static IEqualityComparer<string> Comparer => _lazyComparer.Value;
private class FilePathNormalizingComparer : IEqualityComparer<string>
{
public bool Equals(string? x, string? y) => FilePathNormalizer.FilePathsEquivalent(x, y);
public int GetHashCode([DisallowNull] string obj) => FilePathNormalizer.GetHashCode(obj);
}
public static string NormalizeDirectory(string? directoryFilePath)
{
if (directoryFilePath.IsNullOrEmpty())
@ -118,6 +131,28 @@ internal static class FilePathNormalizer
return normalizedSpan1.Equals(normalizedSpan2, FilePathComparison.Instance);
}
public static int GetHashCode(string filePath)
{
if (filePath.Length == 0)
{
return filePath.GetHashCode();
}
var filePathSpan = filePath.AsSpanOrDefault();
using var _ = ArrayPool<char>.Shared.GetPooledArray(filePathSpan.Length, out var array1);
var normalizedSpan = NormalizeCoreAndGetSpan(filePathSpan, array1);
var hashCombiner = HashCodeCombiner.Start();
foreach (var ch in normalizedSpan)
{
hashCombiner.Add(ch);
}
return hashCombiner.CombinedHash;
}
private static ReadOnlySpan<char> NormalizeCoreAndGetSpan(ReadOnlySpan<char> source, Span<char> destination)
{
var (start, length) = NormalizeCore(source, destination);

Просмотреть файл

@ -43,4 +43,6 @@ internal abstract class LanguageServerFeatureOptions
/// razor/monitorProjectConfigurationFilePath notification is sent.
/// </remarks>
public abstract bool MonitorWorkspaceFolderForConfigurationFiles { get; }
public abstract bool UseRazorCohostServer { get; }
}

Просмотреть файл

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.ProjectSystem;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis.CSharp;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem;
@ -23,7 +24,7 @@ internal class ProjectSnapshot : IProjectSnapshot
State = state ?? throw new ArgumentNullException(nameof(state));
_lock = new object();
_documents = new Dictionary<string, DocumentSnapshot>(FilePathComparer.Instance);
_documents = new Dictionary<string, DocumentSnapshot>(FilePathNormalizer.Comparer);
}
public ProjectKey Key => State.HostProject.Key;

Просмотреть файл

@ -9,6 +9,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.ProjectSystem;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
@ -28,8 +29,8 @@ internal class ProjectState
ProjectDifference.DocumentAdded |
ProjectDifference.DocumentRemoved;
private static readonly ImmutableDictionary<string, DocumentState> s_emptyDocuments = ImmutableDictionary.Create<string, DocumentState>(FilePathComparer.Instance);
private static readonly ImmutableDictionary<string, ImmutableArray<string>> s_emptyImportsToRelatedDocuments = ImmutableDictionary.Create<string, ImmutableArray<string>>(FilePathComparer.Instance);
private static readonly ImmutableDictionary<string, DocumentState> s_emptyDocuments = ImmutableDictionary.Create<string, DocumentState>(FilePathNormalizer.Comparer);
private static readonly ImmutableDictionary<string, ImmutableArray<string>> s_emptyImportsToRelatedDocuments = ImmutableDictionary.Create<string, ImmutableArray<string>>(FilePathNormalizer.Comparer);
private readonly object _lock;
private readonly ProjectSnapshotProjectEngineFactory _projectEngineFactory;
@ -357,7 +358,7 @@ internal class ProjectState
return this;
}
var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithConfigurationChange(), FilePathComparer.Instance);
var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithConfigurationChange(), FilePathNormalizer.Comparer);
// If the host project has changed then we need to recompute the imports map
var importsToRelatedDocuments = s_emptyImportsToRelatedDocuments;
@ -385,7 +386,7 @@ internal class ProjectState
}
var difference = ProjectDifference.ProjectWorkspaceStateChanged;
var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithProjectWorkspaceStateChange(), FilePathComparer.Instance);
var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithProjectWorkspaceStateChange(), FilePathNormalizer.Comparer);
var state = new ProjectState(this, difference, HostProject, projectWorkspaceState, documents, ImportsToRelatedDocuments);
return state;
}
@ -441,7 +442,7 @@ internal class ProjectState
{
var itemTargetPath = importItem.FilePath.Replace('/', '\\').TrimStart('\\');
if (FilePathComparer.Instance.Equals(itemTargetPath, hostDocument.TargetPath))
if (FilePathNormalizer.Comparer.Equals(itemTargetPath, hostDocument.TargetPath))
{
// We've normalized the original importItem.FilePath into the HostDocument.TargetPath. For instance, if the HostDocument.TargetPath
// was '/_Imports.razor' it'd be normalized down into '_Imports.razor'. The purpose of this method is to get the associated document

Просмотреть файл

@ -0,0 +1,59 @@
// 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.AspNetCore.Razor.LanguageServer;
using Microsoft.AspNetCore.Razor.LanguageServer.DocumentColor;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.LanguageServerClient.Razor.Extensions;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Cohost;
[LanguageServerEndpoint(Methods.TextDocumentDocumentColorName)]
[ExportRazorStatelessLspService(typeof(CohostDocumentColorEndpoint))]
[Export(typeof(ICapabilitiesProvider))]
[method: ImportingConstructor]
internal sealed class CohostDocumentColorEndpoint(
IDocumentColorService documentColorService,
IDocumentContextFactory documentContextFactory,
IRazorLoggerFactory loggerFactory)
: AbstractRazorCohostDocumentRequestHandler<DocumentColorParams, ColorInformation[]>, ICapabilitiesProvider
{
private readonly IDocumentColorService _documentColorService = documentColorService;
private readonly IDocumentContextFactory _documentContextFactory = documentContextFactory;
private readonly ILogger _logger = loggerFactory.CreateLogger<CohostDocumentColorEndpoint>();
protected override bool MutatesSolutionState => false;
protected override bool RequiresLSPSolution => true;
protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(DocumentColorParams request)
=> request.TextDocument.ToRazorTextDocumentIdentifier();
public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
=> _documentColorService.ApplyCapabilities(serverCapabilities, clientCapabilities);
protected override Task<ColorInformation[]> HandleRequestAsync(DocumentColorParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
{
// TODO: Create document context from request.TextDocument, by looking at request.Solution instead of our project snapshots
var documentContext = _documentContextFactory.TryCreateForOpenDocument(request.TextDocument);
_logger.LogDebug("[Cohost] Received document color request for {requestPath} and got document {documentPath}", request.TextDocument.Uri, documentContext?.FilePath);
// TODO: We can't MEF import IRazorCohostClientLanguageServerManager in the constructor. We can make this work
// by having it implement a base class, RazorClientConnectionBase or something, that in turn implements
// AbstractRazorLspService (defined in Roslyn) and then move everything from importing IClientConnection
// to importing the new base class, so we can continue to share services.
//
// Until then we have to get the service from the request context.
var clientLanguageServerManager = context.GetRequiredService<IRazorCohostClientLanguageServerManager>();
var clientConnection = new RazorCohostClientConnection(clientLanguageServerManager);
return _documentColorService.GetColorInformationAsync(clientConnection, request, documentContext, cancellationToken);
}
}

Просмотреть файл

@ -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;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Cohost;
[Export(typeof(IRazorCohostDidChangeHandler)), Shared]
[method: ImportingConstructor]
internal class DidChangeHandler(ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher, RazorProjectService razorProjectService) : IRazorCohostDidChangeHandler
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
private readonly RazorProjectService _razorProjectService = razorProjectService;
public Task HandleAsync(Uri uri, int version, SourceText sourceText, CancellationToken cancellationToken)
{
return _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
() => _razorProjectService.UpdateDocument(uri.GetAbsoluteOrUNCPath(), sourceText, version),
cancellationToken);
}
}

Просмотреть файл

@ -0,0 +1,27 @@
// 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 System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Cohost;
[Export(typeof(IRazorCohostDidCloseHandler)), Shared]
[method: ImportingConstructor]
internal class DidCloseHandler(ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher, RazorProjectService razorProjectService) : IRazorCohostDidCloseHandler
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
private readonly RazorProjectService _razorProjectService = razorProjectService;
public Task HandleAsync(Uri uri, CancellationToken cancellationToken)
{
return _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
() => _razorProjectService.CloseDocument(uri.GetAbsoluteOrUNCPath()),
cancellationToken);
}
}

Просмотреть файл

@ -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;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Cohost;
[Export(typeof(IRazorCohostDidOpenHandler)), Shared]
[method: ImportingConstructor]
internal class DidOpenHandler(ProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher, RazorProjectService razorProjectService) : IRazorCohostDidOpenHandler
{
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher = projectSnapshotManagerDispatcher;
private readonly RazorProjectService _razorProjectService = razorProjectService;
public Task HandleAsync(Uri uri, int version, SourceText sourceText, CancellationToken cancellationToken)
{
return _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(
() => _razorProjectService.OpenDocument(uri.GetAbsoluteOrUNCPath(), sourceText, version),
cancellationToken);
}
}

Просмотреть файл

@ -0,0 +1,33 @@
// 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.ComponentModel.Composition;
using Microsoft.AspNetCore.Razor.LanguageServer;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Cohost;
[Export(typeof(IRazorCohostCapabilitiesProvider)), System.Composition.Shared]
[method: ImportingConstructor]
internal class RazorCohostCapabilitiesProvider([ImportMany(typeof(ICapabilitiesProvider))] IEnumerable<Lazy<ICapabilitiesProvider>> cohostCapabilitiesProviders) : IRazorCohostCapabilitiesProvider
{
private readonly IEnumerable<Lazy<ICapabilitiesProvider>> _cohostCapabilitiesProviders = cohostCapabilitiesProviders;
public string GetCapabilities(string clientCapabilities)
{
var clientCapabilitiesObject = JsonConvert.DeserializeObject<VSInternalClientCapabilities>(clientCapabilities) ?? new();
var serverCapabilities = new VSInternalServerCapabilities();
foreach (var provider in _cohostCapabilitiesProviders)
{
provider.Value.ApplyCapabilities(serverCapabilities, clientCapabilitiesObject);
}
return JsonConvert.SerializeObject(serverCapabilities);
}
}

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Cohost;
internal sealed class RazorCohostClientConnection(IRazorCohostClientLanguageServerManager clientNotifier) : IClientConnection
{
private readonly IRazorCohostClientLanguageServerManager _clientNotifier = clientNotifier;
public Task SendNotificationAsync<TParams>(string method, TParams @params, CancellationToken cancellationToken)
=> _clientNotifier.SendNotificationAsync<TParams>(method, @params, cancellationToken).AsTask();
public Task SendNotificationAsync(string method, CancellationToken cancellationToken)
=> _clientNotifier.SendNotificationAsync(method, cancellationToken).AsTask();
public Task<TResponse> SendRequestAsync<TParams, TResponse>(string method, TParams @params, CancellationToken cancellationToken)
=> _clientNotifier.SendRequestAsync<TParams, TResponse>(method, @params, cancellationToken);
}

Просмотреть файл

@ -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.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor.Workspaces;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Cohost;
[Export(typeof(IRazorCohostLanguageClientActivationService)), Shared]
[method: ImportingConstructor]
internal class RazorCohostLanguageClientActivationService(LanguageServerFeatureOptions languageServerFeatureOptions) : IRazorCohostLanguageClientActivationService
{
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
public bool ShouldActivateCohostServer()
{
return _languageServerFeatureOptions.UseRazorCohostServer;
}
}

Просмотреть файл

@ -2,9 +2,11 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.VisualStudio.LanguageServerClient.Razor.Extensions;
internal static class TextDocumentExtensions
{
/// <summary>
@ -15,4 +17,7 @@ internal static class TextDocumentExtensions
textDocumentIdentifier.Uri = uri;
return textDocumentIdentifier;
}
public static RazorTextDocumentIdentifier ToRazorTextDocumentIdentifier(this TextDocumentIdentifier textDocumentIdentifier)
=> new RazorTextDocumentIdentifier(textDocumentIdentifier.Uri, (textDocumentIdentifier as VSTextDocumentIdentifier)?.ProjectContext?.Id);
}

Просмотреть файл

@ -27,6 +27,8 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsPackageVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.ExternalAccess.Razor" Version="$(MicrosoftCodeAnalysisExternalAccessRazorPackageVersion)" />
<!--
Pinning packages to avoid misaligned reference CI failures.
CI builds here: https://github.com/dotnet/razor-tooling/issues/4327

Просмотреть файл

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.AspNetCore.Razor.LanguageServer.Protocol;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Workspaces;
@ -24,8 +25,9 @@ using static Microsoft.VisualStudio.LanguageServer.ContainedLanguage.DefaultLSPD
namespace Microsoft.VisualStudio.LanguageServerClient.Razor;
[Export(typeof(IRazorCustomMessageTarget))]
[Export(typeof(RazorCustomMessageTarget))]
internal partial class RazorCustomMessageTarget
internal partial class RazorCustomMessageTarget : IRazorCustomMessageTarget
{
private readonly TrackingLSPDocumentManager _documentManager;
private readonly JoinableTaskFactory _joinableTaskFactory;

Просмотреть файл

@ -15,11 +15,13 @@ internal class VisualStudioWindowsLanguageServerFeatureOptions : LanguageServerF
private const string ShowAllCSharpCodeActionsFeatureFlag = "Razor.LSP.ShowAllCSharpCodeActions";
private const string IncludeProjectKeyInGeneratedFilePathFeatureFlag = "Razor.LSP.IncludeProjectKeyInGeneratedFilePath";
private const string UsePreciseSemanticTokenRangesFeatureFlag = "Razor.LSP.UsePreciseSemanticTokenRanges";
private const string UseRazorCohostServerFeatureFlag = "Razor.LSP.UseRazorCohostServer";
private readonly LSPEditorFeatureDetector _lspEditorFeatureDetector;
private readonly Lazy<bool> _showAllCSharpCodeActions;
private readonly Lazy<bool> _includeProjectKeyInGeneratedFilePath;
private readonly Lazy<bool> _usePreciseSemanticTokenRanges;
private readonly Lazy<bool> _useRazorCohostServer;
[ImportingConstructor]
public VisualStudioWindowsLanguageServerFeatureOptions(LSPEditorFeatureDetector lspEditorFeatureDetector)
@ -51,6 +53,13 @@ internal class VisualStudioWindowsLanguageServerFeatureOptions : LanguageServerF
var usePreciseSemanticTokenRanges = featureFlags.IsFeatureEnabled(UsePreciseSemanticTokenRangesFeatureFlag, defaultValue: false);
return usePreciseSemanticTokenRanges;
});
_useRazorCohostServer = new Lazy<bool>(() =>
{
var featureFlags = (IVsFeatureFlags)AsyncPackage.GetGlobalService(typeof(SVsFeatureFlags));
var useRazorCohostServer = featureFlags.IsFeatureEnabled(UseRazorCohostServerFeatureFlag, defaultValue: false);
return useRazorCohostServer;
});
}
// We don't currently support file creation operations on VS Codespaces or VS Liveshare
@ -82,4 +91,6 @@ internal class VisualStudioWindowsLanguageServerFeatureOptions : LanguageServerF
public override bool UsePreciseSemanticTokenRanges => _usePreciseSemanticTokenRanges.Value;
public override bool MonitorWorkspaceFolderForConfigurationFiles => false;
public override bool UseRazorCohostServer => _useRazorCohostServer.Value;
}

Просмотреть файл

@ -62,3 +62,9 @@
"Title"="Use precise semantic token ranges when requesting semantic token information on Razor files (requires restart)"
"PreviewPaneChannels"="*"
"VisibleToInternalUsersOnlyChannels"="*"
[$RootKey$\FeatureFlags\Razor\LSP\UseRazorCohostServer]
"Description"="Uses the Razor language server that is cohosted in Roslyn to provide some Razor tooling functionality."
"Value"=dword:00000000
"Title"="Use Roslyn Cohost server for Razor (requires restart)"
"PreviewPaneChannels"="IntPreview,int.main"

Просмотреть файл

@ -50,6 +50,7 @@
<Asset Type="Microsoft.VisualStudio.Assembly" Path="System.Runtime.CompilerServices.Unsafe.dll" />
<Asset Type="Microsoft.VisualStudio.Assembly" Path="System.Threading.Channels.dll" />
<Asset Type="Microsoft.VisualStudio.Assembly" Path="Microsoft.AspNetCore.Razor.LanguageServer.dll" />
<Asset Type="Microsoft.VisualStudio.MefComponent" Path="Microsoft.AspNetCore.Razor.LanguageServer.dll" />
<Asset Type="Microsoft.VisualStudio.Assembly" Path="Microsoft.AspNetCore.Razor.LanguageSupport.dll" />
<Asset Type="Microsoft.VisualStudio.Assembly" Path="Microsoft.AspNetCore.Razor.LanguageServer.Protocol.dll" />
<Asset Type="Microsoft.VisualStudio.MefComponent" Path="Microsoft.CodeAnalysis.Razor.Compiler.Language.dll" />

Просмотреть файл

@ -140,7 +140,7 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
await ValidateCodeActionAsync(input, expected, RazorPredefinedCodeRefactoringProviderNames.IntroduceVariable);
}
[Fact]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/71335")]
public async Task Handle_IntroduceLocal_All()
{
var input = """

Просмотреть файл

@ -45,4 +45,6 @@ internal class TestLanguageServerFeatureOptions : LanguageServerFeatureOptions
public override bool IncludeProjectKeyInGeneratedFilePath => _includeProjectKeyInGeneratedFilePath;
public override bool MonitorWorkspaceFolderForConfigurationFiles => _monitorWorkspaceFolderForConfigurationFiles;
public override bool UseRazorCohostServer => throw new System.NotImplementedException();
}

Просмотреть файл

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Test.Common.Editor;
@ -387,9 +388,9 @@ public class FallbackWindowsRazorProjectHostTest : ProjectSnapshotManagerDispatc
Assert.Same(FallbackRazorConfiguration.MVC_2_0, snapshot.Configuration);
Assert.Collection(
snapshot.DocumentFilePaths,
filePath => Assert.Equal("C:\\Path\\Index.cshtml", filePath),
filePath => Assert.Equal("C:\\Path\\About.cshtml", filePath));
snapshot.DocumentFilePaths.OrderBy(d => d),
filePath => Assert.Equal("C:\\Path\\About.cshtml", filePath),
filePath => Assert.Equal("C:\\Path\\Index.cshtml", filePath));
await Task.Run(async () => await host.DisposeAsync());
Assert.Empty(_projectManager.GetProjects());