diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 1966e0ef10d..cf1ad4498bd 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -7,27 +7,27 @@ This file should be imported by eng/Versions.props - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 - 5.0.0-2.25380.11 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 + 5.0.0-2.25406.1 9.0.0-beta.25255.5 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0165310fb90..2c7386660b0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -2,89 +2,89 @@ - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca - + https://github.com/dotnet/roslyn - 512fe5197056a877a86dff3054d8bd0028af13ed + 5759a992418f0fd3e206cc6f369b6f33e873dfca diff --git a/eng/targets/Services.props b/eng/targets/Services.props index c1eebc01466..1a90cd2d728 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -42,5 +42,6 @@ + diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CodeLens/CohostCodeLensEndpoint.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CodeLens/CohostCodeLensEndpoint.cs new file mode 100644 index 00000000000..ff84beda1fe --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CodeLens/CohostCodeLensEndpoint.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; +using Microsoft.CodeAnalysis.Razor.Cohost; +using Microsoft.CodeAnalysis.Razor.Remote; + +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +#pragma warning disable RS0030 // Do not use banned APIs +[Shared] +[CohostEndpoint(Methods.TextDocumentCodeLensName)] +[Export(typeof(IDynamicRegistrationProvider))] +[ExportRazorStatelessLspService(typeof(CohostCodeLensEndpoint))] +[method: ImportingConstructor] +#pragma warning restore RS0030 // Do not use banned APIs +internal sealed class CohostCodeLensEndpoint( + IIncompatibleProjectService incompatibleProjectService, + IRemoteServiceInvoker remoteServiceInvoker) + : AbstractCohostDocumentEndpoint(incompatibleProjectService), IDynamicRegistrationProvider +{ + private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker; + + protected override bool MutatesSolutionState => false; + protected override bool RequiresLSPSolution => true; + + public ImmutableArray GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext) + { + if (clientCapabilities.TextDocument?.CodeLens?.DynamicRegistration is true) + { + return [new Registration() + { + Method = Methods.TextDocumentCodeLensName, + RegisterOptions = new CodeLensRegistrationOptions() + { + ResolveProvider = true + } + }]; + } + + return []; + } + + protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(CodeLensParams request) + => request.TextDocument.ToRazorTextDocumentIdentifier(); + + protected override async Task HandleRequestAsync(CodeLensParams request, TextDocument razorDocument, CancellationToken cancellationToken) + { + var result = await _remoteServiceInvoker.TryInvokeAsync( + razorDocument.Project.Solution, + (service, solutionInfo, cancellationToken) => service.GetCodeLensAsync(solutionInfo, razorDocument.Id, request.TextDocument, cancellationToken), + cancellationToken).ConfigureAwait(false); + + return result; + } + + internal TestAccessor GetTestAccessor() => new(this); + + internal readonly struct TestAccessor(CohostCodeLensEndpoint instance) + { + public Task HandleRequestAsync(CodeLensParams request, TextDocument razorDocument, CancellationToken cancellationToken) + => instance.HandleRequestAsync(request, razorDocument, cancellationToken); + } +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CodeLens/CohostResolveCodeLensEndpoint.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CodeLens/CohostResolveCodeLensEndpoint.cs new file mode 100644 index 00000000000..81f0a9600bd --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CodeLens/CohostResolveCodeLensEndpoint.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; +using Microsoft.CodeAnalysis.Razor.Cohost; +using Microsoft.CodeAnalysis.Razor.Remote; +using Microsoft.CodeAnalysis.Razor.Workspaces.CodeLens; + +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +#pragma warning disable RS0030 // Do not use banned APIs +[Shared] +[CohostEndpoint(Methods.CodeLensResolveName)] +[ExportRazorStatelessLspService(typeof(CohostResolveCodeLensEndpoint))] +[method: ImportingConstructor] +#pragma warning restore RS0030 // Do not use banned APIs +internal sealed class CohostResolveCodeLensEndpoint( + IIncompatibleProjectService incompatibleProjectService, + IRemoteServiceInvoker remoteServiceInvoker) + : AbstractCohostDocumentEndpoint(incompatibleProjectService) +{ + private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker; + + protected override bool MutatesSolutionState => false; + protected override bool RequiresLSPSolution => true; + + protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(LspCodeLens request) + => RazorCodeLensResolveData.Unwrap(request).TextDocument.ToRazorTextDocumentIdentifier(); + + protected override async Task HandleRequestAsync(LspCodeLens request, TextDocument razorDocument, CancellationToken cancellationToken) + { + var result = await _remoteServiceInvoker.TryInvokeAsync( + razorDocument.Project.Solution, + (service, solutionInfo, cancellationToken) => service.ResolveCodeLensAsync(solutionInfo, razorDocument.Id, request, cancellationToken), + cancellationToken).ConfigureAwait(false); + + return result; + } + + internal TestAccessor GetTestAccessor() => new(this); + + internal readonly struct TestAccessor(CohostResolveCodeLensEndpoint instance) + { + public RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(LspCodeLens request) + => instance.GetRazorTextDocumentIdentifier(request); + + public Task HandleRequestAsync(LspCodeLens request, TextDocument razorDocument, CancellationToken cancellationToken) + => instance.HandleRequestAsync(request, razorDocument, cancellationToken); + } +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CohostStartupService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CohostStartupService.cs index e9e40205ad0..89f9a6675cd 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CohostStartupService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/CohostStartupService.cs @@ -14,11 +14,13 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; +#pragma warning disable CS0618 // Type or member is obsolete. Will be addressed in https://github.com/dotnet/razor/pull/12079 but Roslyn changes are batched [Export(typeof(ICohostStartupService))] [method: ImportingConstructor] internal sealed class CohostStartupService( [ImportMany] IEnumerable> lazyStartupServices, ILoggerFactory loggerFactory) : ICohostStartupService +#pragma warning restore CS0618 // Type or member is obsolete { private readonly ImmutableArray> _lazyStartupServices = [.. lazyStartupServices]; private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/Microsoft.CodeAnalysis.Razor.CohostingShared.projitems b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/Microsoft.CodeAnalysis.Razor.CohostingShared.projitems index 31425b94a4f..9737517b6da 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/Microsoft.CodeAnalysis.Razor.CohostingShared.projitems +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/Microsoft.CodeAnalysis.Razor.CohostingShared.projitems @@ -11,6 +11,8 @@ + + diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeLens/RazorCodeLensResolveData.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeLens/RazorCodeLensResolveData.cs new file mode 100644 index 00000000000..0c95f08499a --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeLens/RazorCodeLensResolveData.cs @@ -0,0 +1,36 @@ +// 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.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.CodeAnalysis.Razor.Workspaces.CodeLens; + +internal record RazorCodeLensResolveData( + // NOTE: Uppercase T here is required to match Roslyn's DocumentResolveData structure, so that the Roslyn + // language server can correctly route requests to us in cohosting. In future when we normalize + // on to Roslyn types, we should inherit from that class so we don't have to remember to do this. + [property: JsonPropertyName("TextDocument")] TextDocumentIdentifier TextDocument, + [property: JsonPropertyName("data")] object? OriginalData) +{ + public static RazorCodeLensResolveData Unwrap(LspCodeLens codeLens) + { + if (codeLens.Data is not JsonElement paramsObj) + { + throw new InvalidOperationException($"Invalid completion item received'{codeLens}'."); + } + + if (paramsObj.Deserialize() is not { } data) + { + throw new InvalidOperationException($"codeLens.Data should be convertible to {nameof(RazorCodeLensResolveData)}"); + } + + return data; + } + + public static void Wrap(LspCodeLens codeLens, TextDocumentIdentifier textDocument) + { + codeLens.Data = new RazorCodeLensResolveData(textDocument, codeLens.Data); + } +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/GlobalUsings.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/GlobalUsings.cs index fe8f0f54995..c96b62ec2a0 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/GlobalUsings.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/GlobalUsings.cs @@ -8,6 +8,7 @@ // makes for a more pleasant development experience. // Avoid extern alias in every file that needs to use Range +global using LspCodeLens = Roslyn.LanguageServer.Protocol.CodeLens; global using LspColorPresentation = Roslyn.LanguageServer.Protocol.ColorPresentation; global using LspDiagnostic = Roslyn.LanguageServer.Protocol.Diagnostic; global using LspDiagnosticSeverity = Roslyn.LanguageServer.Protocol.DiagnosticSeverity; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteCodeLensService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteCodeLensService.cs new file mode 100644 index 00000000000..85bd1b45a16 --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteCodeLensService.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; + +namespace Microsoft.CodeAnalysis.Razor.Remote; + +internal interface IRemoteCodeLensService : IRemoteJsonService +{ + ValueTask GetCodeLensAsync( + JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, + JsonSerializableDocumentId razorDocumentId, + TextDocumentIdentifier textDocumentIdentifier, + CancellationToken cancellationToken); + + ValueTask ResolveCodeLensAsync( + JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, + JsonSerializableDocumentId razorDocumentId, + LspCodeLens codeLens, + CancellationToken cancellationToken); +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs index 01eaf6df6e7..90bd0fb1efa 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs @@ -45,6 +45,7 @@ internal static class RazorServices (typeof(IRemoteCodeActionsService), null), (typeof(IRemoteFindAllReferencesService), null), (typeof(IRemoteMEFInitializationService), null), + (typeof(IRemoteCodeLensService), null), ]; private const string ComponentName = "Razor"; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/CodeLens/RemoteCodeLensService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/CodeLens/RemoteCodeLensService.cs new file mode 100644 index 00000000000..3ea5027518d --- /dev/null +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/CodeLens/RemoteCodeLensService.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.PooledObjects; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Razor.DocumentMapping; +using Microsoft.CodeAnalysis.Razor.Remote; +using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; + +namespace Microsoft.CodeAnalysis.Remote.Razor; + +internal class RemoteCodeLensService(in ServiceArgs args) : RazorDocumentServiceBase(args), IRemoteCodeLensService +{ + internal sealed class Factory : FactoryBase + { + protected override IRemoteCodeLensService CreateService(in ServiceArgs args) + => new RemoteCodeLensService(in args); + } + + private readonly IDocumentMappingService _documentMappingService = args.ExportProvider.GetExportedValue(); + + public ValueTask GetCodeLensAsync( + JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, + JsonSerializableDocumentId razorDocumentId, + TextDocumentIdentifier textDocumentIdentifier, + CancellationToken cancellationToken) + => RunServiceAsync( + solutionInfo, + razorDocumentId, + context => GetCodeLensAsync(context, textDocumentIdentifier, cancellationToken), + cancellationToken); + + private async ValueTask GetCodeLensAsync( + RemoteDocumentContext context, + TextDocumentIdentifier textDocumentIdentifier, + CancellationToken cancellationToken) + { + var snapshot = context.Snapshot; + var generatedDocument = await snapshot.GetGeneratedDocumentAsync(cancellationToken).ConfigureAwait(false); + + var csharpCodeLens = await ExternalAccess.Razor.Cohost.Handlers.CodeLens.GetCodeLensAsync(textDocumentIdentifier, generatedDocument, cancellationToken).ConfigureAwait(false); + + if (csharpCodeLens is null) + { + return null; + } + + var codeDocument = await context.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false); + var csharpDocument = codeDocument.GetCSharpDocument(); + if (csharpDocument is null) + { + return null; + } + + using var results = new PooledArrayBuilder(csharpCodeLens.Length); + + foreach (var codeLens in csharpCodeLens) + { + if (_documentMappingService.TryMapToRazorDocumentRange(csharpDocument, codeLens.Range, out var razorRange)) + { + codeLens.Range = razorRange; + results.Add(codeLens); + } + } + + return results.ToArrayAndClear(); + } + + public ValueTask ResolveCodeLensAsync( + JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, + JsonSerializableDocumentId razorDocumentId, + LspCodeLens codeLens, + CancellationToken cancellationToken) + => RunServiceAsync( + solutionInfo, + razorDocumentId, + context => ResolveCodeLensAsync(context, codeLens, cancellationToken), + cancellationToken); + + private async ValueTask ResolveCodeLensAsync(RemoteDocumentContext context, LspCodeLens codeLens, CancellationToken cancellationToken) + { + var snapshot = context.Snapshot; + var generatedDocument = await snapshot.GetGeneratedDocumentAsync(cancellationToken).ConfigureAwait(false); + + return await ExternalAccess.Razor.Cohost.Handlers.CodeLens.ResolveCodeLensAsync(codeLens, generatedDocument, cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs index 48a304b5a52..2569112203a 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs @@ -204,6 +204,7 @@ private async ValueTask GetCompletionAsync( VSInternalCompletionList? completionList = null; using (_telemetryReporter.TrackLspRequest(Methods.TextDocumentCompletionName, Constants.ExternalAccessServerName, TelemetryThresholds.CompletionSubLSPTelemetryThreshold, correlationId)) { +#pragma warning disable CS0618 // Type or member is obsolete. Will be addressed in a future PR but Roslyn changes are batched completionList = await ExternalAccess.Razor.Cohost.Handlers.Completion.GetCompletionListAsync( generatedDocument, mappedLinePosition, @@ -212,6 +213,7 @@ private async ValueTask GetCompletionAsync( completionSetting, cancellationToken) .ConfigureAwait(false); +#pragma warning restore CS0618 // Type or member is obsolete } if (completionList is null) @@ -328,12 +330,14 @@ private async ValueTask ResolveCSharpCompletionItemAsy var clientCapabilities = _clientCapabilitiesService.ClientCapabilities; var completionListSetting = clientCapabilities.TextDocument?.Completion; +#pragma warning disable CS0618 // Type or member is obsolete. Will be addressed in a future PR but Roslyn changes are batched var result = await ExternalAccess.Razor.Cohost.Handlers.Completion.ResolveCompletionItemAsync( request, generatedDocument, clientCapabilities.SupportsVisualStudioExtensions, completionListSetting ?? new(), cancellationToken).ConfigureAwait(false); +#pragma warning restore CS0618 // Type or member is obsolete var item = JsonHelpers.Convert(result).AssumeNotNull(); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/InlayHints/RemoteInlayHintService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/InlayHints/RemoteInlayHintService.cs index 40b123d2fb9..c6aba289f59 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/InlayHints/RemoteInlayHintService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/InlayHints/RemoteInlayHintService.cs @@ -67,8 +67,9 @@ protected override IRemoteInlayHintService CreateService(in ServiceArgs args) var textDocument = inlayHintParams.TextDocument.WithUri(generatedDocument.CreateUri()); var range = projectedLinePositionSpan.ToRange(); +#pragma warning disable CS0618 // Type or member is obsolete. Will be addressed in a future PR but Roslyn changes are batched var hints = await InlayHints.GetInlayHintsAsync(generatedDocument, textDocument, range, displayAllOverride, cancellationToken).ConfigureAwait(false); - +#pragma warning restore CS0618 // Type or member is obsolete if (hints is null) { return null; @@ -130,6 +131,8 @@ private async ValueTask ResolveInlayHintAsync(RemoteDocumentContext c .GetGeneratedDocumentAsync(cancellationToken) .ConfigureAwait(false); +#pragma warning disable CS0618 // Type or member is obsolete. Will be addressed in a future PR but Roslyn changes are batched return await InlayHints.ResolveInlayHintAsync(generatedDocument, inlayHint, cancellationToken).ConfigureAwait(false); +#pragma warning restore CS0618 // Type or member is obsolete } } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostCodeLensEndpointTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostCodeLensEndpointTest.cs new file mode 100644 index 00000000000..ab116f2fa9f --- /dev/null +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostCodeLensEndpointTest.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Test.Common; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Razor.Protocol; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost; + +public class CohostCodeLensEndpointTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper) +{ + [Fact] + public Task OneMethod() + { + return VerifyCodeLensAsync(""" +
+ + @code { + public void [|{|Position0:|}Method|]() + { + // This is a method + } + } + """, + expectedTitles: ["0 references"]); + } + + [Fact] + public Task TwoMethods() + { + return VerifyCodeLensAsync(""" +
+ + @code { + public void [|{|Position0:|}Method|]() + { + Method2(); + } + + public void [|{|Position1:|}Method2|]() + { + // This is another method + } + } + """, + expectedTitles: ["0 references", "1 reference"]); + } + + [Fact] + public Task UsageInRazor() + { + return VerifyCodeLensAsync(""" +
+ + @Method() + + @code { + public string [|{|Position0:|}Method|]() + { + return "Hello, World!"; + } + } + """, + expectedTitles: ["1 reference"]); + } + + private async Task VerifyCodeLensAsync(TestCode input, string[] expectedTitles) + { + var document = CreateProjectAndRazorDocument(input.Text); + var inputText = SourceText.From(input.Text); + + var endpoint = new CohostCodeLensEndpoint(IncompatibleProjectService, RemoteServiceInvoker); + var resolveEndpoint = new CohostResolveCodeLensEndpoint(IncompatibleProjectService, RemoteServiceInvoker); + + var request = new CodeLensParams() + { + TextDocument = new TextDocumentIdentifier() { DocumentUri = document.CreateDocumentUri() }, + }; + + var result = await endpoint.GetTestAccessor().HandleRequestAsync(request, document, DisposalToken); + + Assert.NotNull(result); + foreach (var (codeLens, i) in result.Select((l, i) => (l, i))) + { + Assert.Contains(inputText.GetTextSpan(codeLens.Range), input.Spans); + + // Resolve expects a JsonElement + codeLens.Data = JsonSerializer.SerializeToElement(codeLens.Data, JsonHelpers.JsonSerializerOptions); + + var tdi = resolveEndpoint.GetTestAccessor().GetRazorTextDocumentIdentifier(codeLens); + Assert.NotNull(tdi); + Assert.Equal(document.CreateUri(), tdi.Value.Uri); + + var resolved = await resolveEndpoint.GetTestAccessor().HandleRequestAsync(codeLens, document, DisposalToken); + + Assert.NotNull(resolved); + Assert.NotNull(resolved.Command); + Assert.NotNull(resolved.Command.Arguments); + Assert.Equal(resolved.Command.Title, expectedTitles[i]); + Assert.Equal("roslyn.client.peekReferences", resolved.Command.CommandIdentifier); + + var documentUri = Assert.IsType(resolved.Command.Arguments[0]); + Assert.Equal(document.CreateDocumentUri(), documentUri); + + var position = Assert.IsType(resolved.Command.Arguments[1]); + Assert.Equal(input.NamedSpans[$"Position{i}"].Single(), inputText.GetTextSpan(position.ToZeroWidthRange())); + } + } +}