Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -46,7 +46,7 @@ internal sealed class CohostDocumentCompletionEndpoint(
CompletionListCache completionListCache,
ITelemetryReporter telemetryReporter,
ILoggerFactory loggerFactory)
: AbstractCohostDocumentEndpoint<CompletionParams, RazorVSInternalCompletionList?>(incompatibleProjectService), IDynamicRegistrationProvider
: AbstractCohostDocumentEndpoint<RazorVSInternalCompletionParams, RazorVSInternalCompletionList?>(incompatibleProjectService), IDynamicRegistrationProvider
{
private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;
private readonly IClientSettingsManager _clientSettingsManager = clientSettingsManager;
Expand Down Expand Up @@ -82,13 +82,12 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
return [];
}

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

protected override async Task<RazorVSInternalCompletionList?> HandleRequestAsync(CompletionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
protected override async Task<RazorVSInternalCompletionList?> HandleRequestAsync(RazorVSInternalCompletionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
{
if (request.Context is null ||
JsonHelpers.Convert<CompletionContext, VSInternalCompletionContext>(request.Context) is not { } completionContext)
if (request.Context is not { } completionContext)
{
_logger.LogError("Completion request context is null");
return null;
Expand Down Expand Up @@ -218,23 +217,23 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
}

private async Task<RazorVSInternalCompletionList?> GetHtmlCompletionListAsync(
CompletionParams request,
RazorVSInternalCompletionParams completionParams,
TextDocument razorDocument,
RazorCompletionOptions razorCompletionOptions,
Guid correlationId,
CancellationToken cancellationToken)
{
var result = await _requestInvoker.MakeHtmlLspRequestAsync<CompletionParams, RazorVSInternalCompletionList>(
var result = await _requestInvoker.MakeHtmlLspRequestAsync<RazorVSInternalCompletionParams, RazorVSInternalCompletionList>(
razorDocument,
Methods.TextDocumentCompletionName,
request,
completionParams,
TelemetryThresholds.CompletionSubLSPTelemetryThreshold,
correlationId,
cancellationToken).ConfigureAwait(false);

var rewrittenResponse = DelegatedCompletionHelper.RewriteHtmlResponse(result, razorCompletionOptions);

var razorDocumentIdentifier = new TextDocumentIdentifierAndVersion(request.TextDocument, Version: 0);
var razorDocumentIdentifier = new TextDocumentIdentifierAndVersion(completionParams.TextDocument, Version: 0);
var resolutionContext = new DelegatedCompletionResolutionContext(razorDocumentIdentifier, RazorLanguageKind.Html, rewrittenResponse.Data ?? rewrittenResponse.ItemDefaults?.Data);
var resultId = _completionListCache.Add(rewrittenResponse, resolutionContext);
rewrittenResponse.SetResultId(resultId, _clientCapabilitiesService.ClientCapabilities);
Expand Down Expand Up @@ -285,7 +284,7 @@ ref builder.AsRef(),
internal readonly struct TestAccessor(CohostDocumentCompletionEndpoint instance)
{
public Task<RazorVSInternalCompletionList?> HandleRequestAsync(
CompletionParams request,
RazorVSInternalCompletionParams request,
TextDocument razorDocument,
CancellationToken cancellationToken)
=> instance.HandleRequestAsync(request, razorDocument, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal sealed class HtmlRequestInvoker(
_logger.LogDebug($"Making LSP request for {method} from {htmlDocument.Uri}{(request is ITextDocumentPositionParams positionParams ? $" at {positionParams.Position}" : "")}, checksum {syncResult.Checksum}.");

// Passing Guid.Empty to this method will mean no tracking
using var _ = _telemetryReporter.TrackLspRequest(Methods.TextDocumentCodeActionName, RazorLSPConstants.HtmlLanguageServerName, threshold, correlationId);
using var _ = _telemetryReporter.TrackLspRequest(method, RazorLSPConstants.HtmlLanguageServerName, threshold, correlationId);

var result = await _requestInvoker.ReinvokeRequestOnServerAsync<TRequest, TResponse?>(
htmlDocument.Snapshot.TextBuffer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,29 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;

internal class TestHtmlRequestInvoker : IHtmlRequestInvoker
{
private readonly Dictionary<string, object?> _htmlResponses;
private readonly Dictionary<string, Func<object, object?>> _getResponses;

public TestHtmlRequestInvoker()
: this(Array.Empty<(string, Func<object, object?>)>())
{
}

public TestHtmlRequestInvoker(params (string method, object? response)[] htmlResponses)
: this(htmlResponses.Select<(string method, object? response), (string, Func<object, object?>)>(kvp => (kvp.method, _ => kvp.response)).ToArray())
{
}

public TestHtmlRequestInvoker(params (string method, Func<object, object?> getResponse)[] htmlResponses)
{
_htmlResponses = htmlResponses.ToDictionary(kvp => kvp.method, kvp => kvp.response);
_getResponses = htmlResponses.ToDictionary(kvp => kvp.method, kvp => kvp.getResponse);
}

public Task<TResponse?> MakeHtmlLspRequestAsync<TRequest, TResponse>(TextDocument razorDocument, string method, TRequest request, TimeSpan threshold, Guid correlationId, CancellationToken cancellationToken) where TRequest : notnull
{
if (_htmlResponses is not null &&
_htmlResponses.TryGetValue(method, out var response))
if (_getResponses is not null &&
_getResponses.TryGetValue(method, out var getResponse))
{
return Task.FromResult((TResponse?)response);
return Task.FromResult((TResponse?)getResponse(request));
}

return Task.FromResult<TResponse?>(default);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
Expand All @@ -12,6 +13,7 @@
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Protocol;
Expand Down Expand Up @@ -346,6 +348,23 @@ This is a Razor document.
unexpectedItemLabels: ["snippet1", "snippet2"]);
}

[Fact]
public async Task HtmlElementNamesCompletion_UsesRazorVSInternalCompletionParams()
{
await VerifyCompletionListParamsTypeAsync(
input: """
This is a Razor document.

<$$1
""",
completionContext: new VSInternalCompletionContext()
{
InvokeKind = VSInternalCompletionInvokeKind.Typing,
TriggerCharacter = "<",
TriggerKind = CompletionTriggerKind.TriggerCharacter
});
}

[Fact]
public async Task HtmlElementDoNotCommitWithSpace()
{
Expand Down Expand Up @@ -792,7 +811,7 @@ private async Task<RazorVSInternalCompletionList> VerifyCompletionListAsync(
NoOpTelemetryReporter.Instance,
LoggerFactory);

var request = new CompletionParams()
var request = new RazorVSInternalCompletionParams()
{
TextDocument = new TextDocumentIdentifier()
{
Expand Down Expand Up @@ -826,7 +845,7 @@ private async Task<RazorVSInternalCompletionList> VerifyCompletionListAsync(

if (!commitElementsWithSpace)
{
Assert.False(result.Items.Any(item => item.CommitCharacters?.First().Contains(" ") ?? false));
Assert.False(result.Items.Any(item => item.CommitCharacters?.First().Contains(' ') ?? false));
}

if (!autoInsertAttributeQuotes)
Expand Down Expand Up @@ -855,6 +874,51 @@ private async Task<RazorVSInternalCompletionList> VerifyCompletionListAsync(
return result;
}

private async Task VerifyCompletionListParamsTypeAsync(
TestCode input,
VSInternalCompletionContext completionContext,
RazorFileKind? fileKind = null)
{
var document = CreateProjectAndRazorDocument(input.Text, fileKind);
var sourceText = await document.GetTextAsync(DisposalToken);

// Assert the request invoker is passed a RazorVSInternalCompletionParams
var requestInvoker = new TestHtmlRequestInvoker((Methods.TextDocumentCompletionName, ValidateArgType));

var languageServerFeatureOptions = new TestLanguageServerFeatureOptions();

var completionListCache = new CompletionListCache();
var endpoint = new CohostDocumentCompletionEndpoint(
IncompatibleProjectService,
RemoteServiceInvoker,
ClientSettingsManager,
ClientCapabilitiesService,
snippetCompletionItemProvider: null,
languageServerFeatureOptions,
requestInvoker,
completionListCache,
NoOpTelemetryReporter.Instance,
LoggerFactory);

var request = new RazorVSInternalCompletionParams()
{
TextDocument = new TextDocumentIdentifier()
{
DocumentUri = document.CreateDocumentUri()
},
Position = sourceText.GetPosition(input.Position),
Context = completionContext
};

await endpoint.GetTestAccessor().HandleRequestAsync(request, document, DisposalToken);

static RazorVSInternalCompletionList? ValidateArgType(object arg)
{
Assert.Equal(typeof(RazorVSInternalCompletionParams), arg.GetType());
return default;
}
}

private async Task VerifyCompletionResolveAsync(CodeAnalysis.TextDocument document, CompletionListCache completionListCache, VSInternalCompletionItem item, string? expected, string expectedResolvedItemDescription)
{
// We expect data to be a JsonElement, so for tests we have to _not_ strongly type
Expand Down Expand Up @@ -917,7 +981,7 @@ private async Task VerifyCompletionResolveAsync(CodeAnalysis.TextDocument docume
}
}

private string? FlattenDescription(ClassifiedTextElement? description)
private static string? FlattenDescription(ClassifiedTextElement? description)
{
if (description is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private async Task VerifyRenamesAsync(
var inputText = await document.GetTextAsync(DisposalToken);
var position = inputText.GetPosition(cursorPosition);

var requestInvoker = new TestHtmlRequestInvoker([(Methods.TextDocumentRenameName, null)]);
var requestInvoker = new TestHtmlRequestInvoker([(Methods.TextDocumentRenameName, (object?)null)]);

var endpoint = new CohostRenameEndpoint(IncompatibleProjectService, RemoteServiceInvoker, requestInvoker);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private async Task VerifySignatureHelpAsync(string input, string expected, bool

ClientSettingsManager.Update(ClientCompletionSettings.Default with { AutoListParams = autoListParams });

var requestInvoker = new TestHtmlRequestInvoker([(Methods.TextDocumentSignatureHelpName, null)]);
var requestInvoker = new TestHtmlRequestInvoker([(Methods.TextDocumentSignatureHelpName, (object?)null)]);

var endpoint = new CohostSignatureHelpEndpoint(IncompatibleProjectService, RemoteServiceInvoker, ClientSettingsManager, requestInvoker);

Expand Down