Create cohost spell check endpoints and services

This commit is contained in:
David Wengier 2024-09-03 08:47:03 +10:00
Родитель 56128c3c26
Коммит 4a44a99239
10 изменённых файлов: 226 добавлений и 10 удалений

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

@ -29,5 +29,6 @@
<ServiceHubService Include="Microsoft.VisualStudio.Razor.GoToDefinition" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteGoToDefinitionService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Rename" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteRenameService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.AutoInsert" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteAutoInsertService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.SpellCheck" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteSpellCheckService+Factory" />
</ItemGroup>
</Project>

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

@ -158,12 +158,13 @@ internal static class IServiceCollectionExtensions
{
services.AddHandlerWithCapabilities<TextDocumentTextPresentationEndpoint>();
services.AddHandlerWithCapabilities<TextDocumentUriPresentationEndpoint>();
services.AddSingleton<ISpellCheckService, SpellCheckService>();
services.AddSingleton<ICSharpSpellCheckService, LspCSharpSpellCheckService>();
services.AddHandlerWithCapabilities<DocumentSpellCheckEndpoint>();
services.AddHandler<WorkspaceSpellCheckEndpoint>();
}
services.AddSingleton<ISpellCheckService, SpellCheckService>();
services.AddSingleton<ICSharpSpellCheckService, LspCSharpSpellCheckService>();
services.AddHandlerWithCapabilities<DocumentSpellCheckEndpoint>();
services.AddHandler<WorkspaceSpellCheckEndpoint>();
services.AddHandlerWithCapabilities<DocumentDidChangeEndpoint>();
services.AddHandler<DocumentDidCloseEndpoint>();

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

@ -36,11 +36,13 @@ internal sealed class DocumentSpellCheckEndpoint(
var data = await _spellCheckService.GetSpellCheckRangeTriplesAsync(documentContext, cancellationToken).ConfigureAwait(false);
return [new VSInternalSpellCheckableRangeReport
{
Ranges =data,
ResultId = Guid.NewGuid().ToString()
}
];
return
[
new VSInternalSpellCheckableRangeReport
{
Ranges =data,
ResultId = Guid.NewGuid().ToString()
}
];
}
}

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

@ -0,0 +1,16 @@
// 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.CodeAnalysis.ExternalAccess.Razor;
namespace Microsoft.CodeAnalysis.Razor.Remote;
internal interface IRemoteSpellCheckService
{
ValueTask<int[]> GetSpellCheckRangeTriplesAsync(
RazorPinnedSolutionInfoWrapper solutionInfo,
DocumentId razorDocumentId,
CancellationToken cancellationToken);
}

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

@ -22,6 +22,7 @@ internal static class RazorServices
(typeof(IRemoteFoldingRangeService), null),
(typeof(IRemoteDocumentHighlightService), null),
(typeof(IRemoteAutoInsertService), null),
(typeof(IRemoteSpellCheckService), null),
];
// Internal for testing

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

@ -0,0 +1,17 @@
// 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.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.SpellCheck;
namespace Microsoft.CodeAnalysis.Remote.Razor.SpellCheck;
[Export(typeof(ISpellCheckService)), Shared]
[method: ImportingConstructor]
internal sealed class OOPSpellCheckService(
ICSharpSpellCheckService csharpSpellCheckService,
IDocumentMappingService documentMappingService)
: SpellCheckService(csharpSpellCheckService, documentMappingService)
{
}

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

@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.SpellCheck;
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
namespace Microsoft.CodeAnalysis.Remote.Razor.SpellCheck;
[Export(typeof(ICSharpSpellCheckService)), Shared]
[method: ImportingConstructor]
internal class RemoteCSharpSpellCheckService() : ICSharpSpellCheckService
{
public async Task<ImmutableArray<SpellCheckRange>> GetCSharpSpellCheckRangesAsync(DocumentContext documentContext, CancellationToken cancellationToken)
{
// We have a razor document, lets find the generated C# document
Debug.Assert(documentContext is RemoteDocumentContext, "This method only works on document snapshots created in the OOP process");
var snapshot = (RemoteDocumentSnapshot)documentContext.Snapshot;
var generatedDocument = await snapshot.GetGeneratedDocumentAsync().ConfigureAwait(false);
var csharpRanges = await ExternalAccess.Razor.Cohost.Handlers.SpellCheck.GetSpellCheckSpansAsync(generatedDocument, cancellationToken).ConfigureAwait(false);
return csharpRanges.SelectAsArray(r => new SpellCheckRange((int)r.Kind, r.StartIndex, r.Length));
}
}

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

@ -0,0 +1,34 @@
// 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.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.CodeAnalysis.Razor.SpellCheck;
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
namespace Microsoft.CodeAnalysis.Remote.Razor;
internal sealed partial class RemoteSpellCheckService(in ServiceArgs args) : RazorDocumentServiceBase(in args), IRemoteSpellCheckService
{
internal sealed class Factory : FactoryBase<IRemoteSpellCheckService>
{
protected override IRemoteSpellCheckService CreateService(in ServiceArgs args)
=> new RemoteSpellCheckService(in args);
}
private readonly ISpellCheckService _spellCheckService = args.ExportProvider.GetExportedValue<ISpellCheckService>();
public ValueTask<int[]> GetSpellCheckRangeTriplesAsync(RazorPinnedSolutionInfoWrapper solutionInfo, DocumentId razorDocumentId, CancellationToken cancellationToken)
=> RunServiceAsync(
solutionInfo,
razorDocumentId,
context => GetSpellCheckRangeTriplesAsync(context, cancellationToken),
cancellationToken);
private async ValueTask<int[]> GetSpellCheckRangeTriplesAsync(RemoteDocumentContext context, CancellationToken cancellationToken)
{
return await _spellCheckService.GetSpellCheckRangeTriplesAsync(context, cancellationToken).ConfigureAwait(false);
}
}

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

@ -0,0 +1,80 @@
// 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;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
#pragma warning disable RS0030 // Do not use banned APIs
[Shared]
[CohostEndpoint(VSInternalMethods.TextDocumentSpellCheckableRangesName)]
[Export(typeof(IDynamicRegistrationProvider))]
[ExportCohostStatelessLspService(typeof(CohostDocumentSpellCheckEndpoint))]
[method: ImportingConstructor]
#pragma warning restore RS0030 // Do not use banned APIs
internal class CohostDocumentSpellCheckEndpoint(
IRemoteServiceInvoker remoteServiceInvoker)
: AbstractRazorCohostDocumentRequestHandler<VSInternalDocumentSpellCheckableParams, VSInternalSpellCheckableRangeReport[]>, IDynamicRegistrationProvider
{
private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;
protected override bool MutatesSolutionState => false;
protected override bool RequiresLSPSolution => true;
public Registration? GetRegistration(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
{
if (clientCapabilities.SupportsVisualStudioExtensions)
{
return new Registration
{
Method = VSInternalMethods.TextDocumentSpellCheckableRangesName,
RegisterOptions = new TextDocumentRegistrationOptions()
{
DocumentSelector = filter
}
};
}
return null;
}
protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(VSInternalDocumentSpellCheckableParams request)
=> request.TextDocument.ToRazorTextDocumentIdentifier();
protected override Task<VSInternalSpellCheckableRangeReport[]> HandleRequestAsync(VSInternalDocumentSpellCheckableParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
=> HandleRequestAsync(context.TextDocument.AssumeNotNull(), cancellationToken);
private async Task<VSInternalSpellCheckableRangeReport[]> HandleRequestAsync(TextDocument razorDocument, CancellationToken cancellationToken)
{
var data = await _remoteServiceInvoker.TryInvokeAsync<IRemoteSpellCheckService, int[]>(
razorDocument.Project.Solution,
(service, solutionInfo, cancellationToken) => service.GetSpellCheckRangeTriplesAsync(solutionInfo, razorDocument.Id, cancellationToken),
cancellationToken).ConfigureAwait(false);
return
[
new VSInternalSpellCheckableRangeReport
{
Ranges = data,
ResultId = Guid.NewGuid().ToString()
}
];
}
internal TestAccessor GetTestAccessor() => new(this);
internal readonly struct TestAccessor(CohostDocumentSpellCheckEndpoint instance)
{
public Task<VSInternalSpellCheckableRangeReport[]> HandleRequestAsync(TextDocument razorDocument, CancellationToken cancellationToken)
=> instance.HandleRequestAsync(razorDocument, cancellationToken);
}
}

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

@ -0,0 +1,34 @@
// 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.Threading;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
#pragma warning disable RS0030 // Do not use banned APIs
[Shared]
[CohostEndpoint(VSInternalMethods.WorkspaceSpellCheckableRangesName)]
[ExportCohostStatelessLspService(typeof(CohostWorkspaceSpellCheckEndpoint))]
[method: ImportingConstructor]
#pragma warning restore RS0030 // Do not use banned APIs
internal class CohostWorkspaceSpellCheckEndpoint(
IRemoteServiceInvoker remoteServiceInvoker)
: AbstractRazorCohostRequestHandler<VSInternalWorkspaceSpellCheckableParams, VSInternalWorkspaceSpellCheckableReport[]>
{
private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;
protected override bool MutatesSolutionState => false;
protected override bool RequiresLSPSolution => false;
// Razor files generally don't do anything at the workspace level
protected override Task<VSInternalWorkspaceSpellCheckableReport[]> HandleRequestAsync(VSInternalWorkspaceSpellCheckableParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
=> SpecializedTasks.EmptyArray<VSInternalWorkspaceSpellCheckableReport>();
}