Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cohost: Support signature help #10595

Merged
merged 21 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ public static SemanticTokensOptions EnableSemanticTokens(this SemanticTokensOpti
return options;
}

public static void EnableSignatureHelp(this VSInternalServerCapabilities serverCapabilities)
{
serverCapabilities.SignatureHelpProvider = new SignatureHelpOptions().EnableSignatureHelp();
}

public static SignatureHelpOptions EnableSignatureHelp(this SignatureHelpOptions options)
{
options.TriggerCharacters = ["(", ",", "<"];
options.RetriggerCharacters = [">", ")"];

return options;
}

public static void EnableHoverProvider(this VSInternalServerCapabilities serverCapabilities)
{
serverCapabilities.HoverProvider = new HoverOptions()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.

// 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;
Expand All @@ -11,7 +12,7 @@
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using LS = Microsoft.VisualStudio.LanguageServer.Protocol;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer.SignatureHelp;

Expand All @@ -22,7 +23,7 @@ internal sealed class SignatureHelpEndpoint(
IClientConnection clientConnection,
RazorLSPOptionsMonitor optionsMonitor,
ILoggerFactory loggerProvider)
: AbstractRazorDelegatingEndpoint<SignatureHelpParams, LS.SignatureHelp?>(
: AbstractRazorDelegatingEndpoint<SignatureHelpParams, LSP.SignatureHelp?>(
languageServerFeatureOptions,
documentMappingService,
clientConnection,
Expand All @@ -33,11 +34,7 @@ internal sealed class SignatureHelpEndpoint(

public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
{
serverCapabilities.SignatureHelpProvider = new SignatureHelpOptions()
{
TriggerCharacters = new[] { "(", ",", "<" },
RetriggerCharacters = new[] { ">", ")" }
};
serverCapabilities.EnableSignatureHelp();
}

protected override Task<IDelegatedParams?> CreateDelegatedParamsAsync(SignatureHelpParams request, RazorRequestContext requestContext, DocumentPositionInfo positionInfo, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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.Linq;
using System.Runtime.Serialization;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.CodeAnalysis.Razor.Workspaces.Protocol.SignatureHelp;

[DataContract]
internal readonly record struct RemoteSignatureHelp(
[property: DataMember(Order = 0)] int? ActiveParameter,
[property: DataMember(Order = 1)] int? ActiveSignature,
[property: DataMember(Order = 2)] RemoteSignatureInformation[] Signatures)
{
public LSP.SignatureHelp ToSignatureHelp()
{
return new LSP.SignatureHelp()
{
ActiveParameter = this.ActiveParameter,
ActiveSignature = this.ActiveSignature,
Signatures = this.Signatures.Select(s => s.ToSignatureInformation()).ToArray()
};
}
}
Original file line number Diff line number Diff line change
@@ -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.Runtime.Serialization;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.CodeAnalysis.Razor.Workspaces.Protocol.SignatureHelp;

[DataContract]
internal readonly record struct RemoteSignatureInformation(
[property: DataMember(Order = 0)] string Label)
{
internal SignatureInformation ToSignatureInformation()
{
return new SignatureInformation()
{
Label = this.Label,
Documentation = new MarkupContent()
{
}
};
}
}
Original file line number Diff line number Diff line change
@@ -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;
using Microsoft.CodeAnalysis.Razor.Workspaces.Protocol.SignatureHelp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.CodeAnalysis.Razor.Remote;

internal interface IRemoteSignatureHelpService
{
ValueTask<RemoteSignatureHelp?> GetSignatureHelpAsync(RazorPinnedSolutionInfoWrapper solutionInfo, DocumentId id, LinePosition linePosition, SignatureHelpTriggerKind triggerKind, string? triggerCharacter, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Razor.Workspaces.Protocol.SignatureHelp;
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.Razor.LanguageClient;
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
using Microsoft.VisualStudio.Razor.LanguageClient.Extensions;

namespace Microsoft.VisualStudio.LanguageServices.Razor.LanguageClient.Cohost;

#pragma warning disable RS0030 // Do not use banned APIs
[Shared]
[CohostEndpoint(Methods.TextDocumentSignatureHelpName)]
[Export(typeof(IDynamicRegistrationProvider))]
[ExportCohostStatelessLspService(typeof(CohostSignatureHelpEndpoint))]
[method: ImportingConstructor]
#pragma warning restore RS0030 // Do not use banned APIs
internal class CohostSignatureHelpEndpoint(
IRemoteServiceProvider remoteServiceProvider,
IHtmlDocumentSynchronizer htmlDocumentSynchronizer,
LSPRequestInvoker requestInvoker,
ILoggerFactory loggerFactory)
: AbstractRazorCohostDocumentRequestHandler<SignatureHelpParams, SignatureHelp?>, IDynamicRegistrationProvider
{
private readonly IRemoteServiceProvider _remoteServiceProvider = remoteServiceProvider;
private readonly IHtmlDocumentSynchronizer _htmlDocumentSynchronizer = htmlDocumentSynchronizer;
private readonly LSPRequestInvoker _requestInvoker = requestInvoker;
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CohostFoldingRangeEndpoint>();

protected override bool MutatesSolutionState => false;

protected override bool RequiresLSPSolution => true;

public Registration? GetRegistration(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
{
if (clientCapabilities.TextDocument?.SignatureHelp?.DynamicRegistration == true)
{
return new Registration()
{
Method = Methods.TextDocumentSignatureHelpName,
RegisterOptions = new SignatureHelpRegistrationOptions()
{
DocumentSelector = filter
}.EnableSignatureHelp()
};
}

return null;
}

protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(SignatureHelpParams request)
=> request.TextDocument.ToRazorTextDocumentIdentifier();

protected async override Task<SignatureHelp?> HandleRequestAsync(SignatureHelpParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
{
var razorDocument = context.TextDocument.AssumeNotNull();

var data = await _remoteServiceProvider.TryInvokeAsync<IRemoteSignatureHelpService, RemoteSignatureHelp?>(
razorDocument.Project.Solution,
(service, solutionInfo, cancellationToken) => service.GetSignatureHelpAsync(solutionInfo, razorDocument.Id, request.Position.ToLinePosition(), request.Context.TriggerKind, request.Context.TriggerCharacter, cancellationToken),
cancellationToken).ConfigureAwait(false);

// If we got a response back, then either Razor or C# wants to do something with this, so we're good to go
if (data is { } signatureHelp)
{
return signatureHelp.ToSignatureHelp();
}

// If we didn't get anything from Razor or Roslyn, lets ask Html what they want to do
var htmlDocument = await _htmlDocumentSynchronizer.TryGetSynchronizedHtmlDocumentAsync(razorDocument, cancellationToken).ConfigureAwait(false);
if (htmlDocument is null)
{
return null;
}

request.TextDocument = request.TextDocument.WithUri(htmlDocument.Uri);

var result = await _requestInvoker.ReinvokeRequestOnServerAsync<SignatureHelpParams, SignatureHelp?>(
htmlDocument.Buffer,
Methods.TextDocumentSignatureHelpName,
RazorLSPConstants.HtmlLanguageServerName,
request,
cancellationToken).ConfigureAwait(false);

return result?.Response;
}
}