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

Cohosted Code Actions Part 2: Re-layout in advance of moving down #11083

Merged
Merged
Show file tree
Hide file tree
Changes from 8 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 @@ -12,8 +12,8 @@
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Razor.Workspaces;
Expand Down Expand Up @@ -53,10 +53,9 @@ public async Task SetupAsync()
razorCodeActionProviders: RazorLanguageServerHost.GetRequiredService<IEnumerable<IRazorCodeActionProvider>>(),
csharpCodeActionProviders: RazorLanguageServerHost.GetRequiredService<IEnumerable<ICSharpCodeActionProvider>>(),
htmlCodeActionProviders: RazorLanguageServerHost.GetRequiredService<IEnumerable<IHtmlCodeActionProvider>>(),
clientConnection: RazorLanguageServerHost.GetRequiredService<IClientConnection>(),
delegatedCodeActionsProvider: RazorLanguageServerHost.GetRequiredService<IDelegatedCodeActionsProvider>(),
languageServerFeatureOptions: RazorLanguageServerHost.GetRequiredService<LanguageServerFeatureOptions>(),
loggerFactory: RazorLanguageServerHost.GetRequiredService<ILoggerFactory>(),
telemetryReporter: null);
telemetryReporter: NoOpTelemetryReporter.Instance);

var projectRoot = Path.Combine(RepoRoot, "src", "Razor", "test", "testapps", "ComponentApp");
var projectFilePath = Path.Combine(projectRoot, "ComponentApp.csproj");
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(
}

var tree = context.CodeDocument.GetSyntaxTree();
var node = tree.Root.FindInnermostNode(context.StartLocation.AbsoluteIndex);
var node = tree.Root.FindInnermostNode(context.StartAbsoluteIndex);
var isInImplicitExpression = node?.AncestorsAndSelf().Any(n => n is CSharpImplicitExpressionSyntax) ?? false;

var allowList = isInImplicitExpression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
Expand All @@ -15,19 +14,20 @@
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;

internal sealed class DefaultCSharpCodeActionResolver(
IClientConnection clientConnection,
IRazorFormattingService razorFormattingService) : CSharpCodeActionResolver(clientConnection)
IDelegatedCodeActionResolver delegatedCodeActionResolver,
IRazorFormattingService razorFormattingService) : ICSharpCodeActionResolver
{
private readonly IDelegatedCodeActionResolver _delegatedCodeActionResolver = delegatedCodeActionResolver;
private readonly IRazorFormattingService _razorFormattingService = razorFormattingService;

public override string Action => LanguageServerConstants.CodeActions.Default;
public string Action => LanguageServerConstants.CodeActions.Default;

public async override Task<CodeAction> ResolveAsync(
public async Task<CodeAction> ResolveAsync(
DocumentContext documentContext,
CodeAction codeAction,
CancellationToken cancellationToken)
{
var resolvedCodeAction = await ResolveCodeActionWithServerAsync(documentContext.GetTextDocumentIdentifier(), documentContext.Snapshot.Version, RazorLanguageKind.CSharp, codeAction, cancellationToken).ConfigureAwait(false);
var resolvedCodeAction = await _delegatedCodeActionResolver.ResolveCodeActionAsync(documentContext.GetTextDocumentIdentifier(), documentContext.Snapshot.Version, RazorLanguageKind.CSharp, codeAction, cancellationToken).ConfigureAwait(false);
if (resolvedCodeAction?.Edit?.DocumentChanges is null)
{
// Unable to resolve code action with server, return original code action
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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.Razor.ProjectSystem;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;

internal interface ICSharpCodeActionResolver : ICodeActionResolver
{
Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ static bool TryGetOwner(RazorCodeActionContext context, [NotNullWhen(true)] out
return false;
}

owner = syntaxTree.Root.FindInnermostNode(context.StartLocation.AbsoluteIndex);
owner = syntaxTree.Root.FindInnermostNode(context.StartAbsoluteIndex);
if (owner is null)
{
Debug.Fail("Owner should never be null.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
Expand All @@ -19,21 +18,22 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
/// Resolves and remaps the code action, without running formatting passes.
/// </summary>
internal sealed class UnformattedRemappingCSharpCodeActionResolver(
IClientConnection clientConnection,
IDocumentMappingService documentMappingService) : CSharpCodeActionResolver(clientConnection)
IDelegatedCodeActionResolver delegatedCodeActionResolver,
IDocumentMappingService documentMappingService) : ICSharpCodeActionResolver
{
private readonly IDelegatedCodeActionResolver _delegatedCodeActionResolver = delegatedCodeActionResolver;
private readonly IDocumentMappingService _documentMappingService = documentMappingService;

public override string Action => LanguageServerConstants.CodeActions.UnformattedRemap;
public string Action => LanguageServerConstants.CodeActions.UnformattedRemap;

public async override Task<CodeAction> ResolveAsync(
public async Task<CodeAction> ResolveAsync(
DocumentContext documentContext,
CodeAction codeAction,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

var resolvedCodeAction = await ResolveCodeActionWithServerAsync(documentContext.GetTextDocumentIdentifier(), documentContext.Snapshot.Version, RazorLanguageKind.CSharp, codeAction, cancellationToken).ConfigureAwait(false);
var resolvedCodeAction = await _delegatedCodeActionResolver.ResolveCodeActionAsync(documentContext.GetTextDocumentIdentifier(), documentContext.Snapshot.Version, RazorLanguageKind.CSharp, codeAction, cancellationToken).ConfigureAwait(false);
if (resolvedCodeAction?.Edit?.DocumentChanges is null)
{
// Unable to resolve code action with server, return original code action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using StreamJsonRpc;

namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;

Expand All @@ -34,22 +32,20 @@ internal sealed class CodeActionEndpoint(
IEnumerable<IRazorCodeActionProvider> razorCodeActionProviders,
IEnumerable<ICSharpCodeActionProvider> csharpCodeActionProviders,
IEnumerable<IHtmlCodeActionProvider> htmlCodeActionProviders,
IClientConnection clientConnection,
IDelegatedCodeActionsProvider delegatedCodeActionsProvider,
LanguageServerFeatureOptions languageServerFeatureOptions,
ILoggerFactory loggerFactory,
ITelemetryReporter? telemetryReporter)
ITelemetryReporter telemetryReporter)
: IRazorRequestHandler<VSCodeActionParams, SumType<Command, CodeAction>[]?>, ICapabilitiesProvider
{
private const string LspEndpointName = Methods.TextDocumentCodeActionName;

private readonly IDocumentMappingService _documentMappingService = documentMappingService ?? throw new ArgumentNullException(nameof(documentMappingService));
private readonly IEnumerable<IRazorCodeActionProvider> _razorCodeActionProviders = razorCodeActionProviders ?? throw new ArgumentNullException(nameof(razorCodeActionProviders));
private readonly IEnumerable<ICSharpCodeActionProvider> _csharpCodeActionProviders = csharpCodeActionProviders ?? throw new ArgumentNullException(nameof(csharpCodeActionProviders));
private readonly IEnumerable<IHtmlCodeActionProvider> _htmlCodeActionProviders = htmlCodeActionProviders ?? throw new ArgumentNullException(nameof(htmlCodeActionProviders));
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions ?? throw new ArgumentNullException(nameof(languageServerFeatureOptions));
private readonly IClientConnection _clientConnection = clientConnection ?? throw new ArgumentNullException(nameof(clientConnection));
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CodeActionEndpoint>();
private readonly ITelemetryReporter? _telemetryReporter = telemetryReporter;
private readonly IDocumentMappingService _documentMappingService = documentMappingService;
private readonly IEnumerable<IRazorCodeActionProvider> _razorCodeActionProviders = razorCodeActionProviders;
private readonly IEnumerable<ICSharpCodeActionProvider> _csharpCodeActionProviders = csharpCodeActionProviders;
private readonly IEnumerable<IHtmlCodeActionProvider> _htmlCodeActionProviders = htmlCodeActionProviders;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
private readonly IDelegatedCodeActionsProvider _delegatedCodeActionsProvider = delegatedCodeActionsProvider;
private readonly ITelemetryReporter _telemetryReporter = telemetryReporter;

internal bool _supportsCodeActionResolve = false;

Expand All @@ -74,12 +70,12 @@ internal sealed class CodeActionEndpoint(
cancellationToken.ThrowIfCancellationRequested();

var correlationId = Guid.NewGuid();
using var __ = _telemetryReporter?.TrackLspRequest(LspEndpointName, LanguageServerConstants.RazorLanguageServerName, correlationId);
using var __ = _telemetryReporter.TrackLspRequest(LspEndpointName, LanguageServerConstants.RazorLanguageServerName, correlationId);
var razorCodeActions = await GetRazorCodeActionsAsync(razorCodeActionContext, cancellationToken).ConfigureAwait(false);

cancellationToken.ThrowIfCancellationRequested();

var delegatedCodeActions = await GetDelegatedCodeActionsAsync(documentContext, razorCodeActionContext, correlationId, cancellationToken).ConfigureAwait(false);
var delegatedCodeActions = await GetDelegatedCodeActionsAsync(razorCodeActionContext, correlationId, cancellationToken).ConfigureAwait(false);

cancellationToken.ThrowIfCancellationRequested();

Expand Down Expand Up @@ -161,12 +157,12 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
request.Range = vsCodeActionContext.SelectionRange;
}

if (!sourceText.TryGetSourceLocation(request.Range.Start, out var startLocation))
if (!sourceText.TryGetAbsoluteIndex(request.Range.Start, out var startLocation))
{
return null;
}

if (!sourceText.TryGetSourceLocation(request.Range.End, out var endLocation))
if (!sourceText.TryGetAbsoluteIndex(request.Range.End, out var endLocation))
{
endLocation = startLocation;
}
Expand All @@ -184,17 +180,17 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
return context;
}

private async Task<ImmutableArray<RazorVSInternalCodeAction>> GetDelegatedCodeActionsAsync(DocumentContext documentContext, RazorCodeActionContext context, Guid correlationId, CancellationToken cancellationToken)
private async Task<ImmutableArray<RazorVSInternalCodeAction>> GetDelegatedCodeActionsAsync(RazorCodeActionContext context, Guid correlationId, CancellationToken cancellationToken)
{
var languageKind = context.CodeDocument.GetLanguageKind(context.StartLocation.AbsoluteIndex, rightAssociative: false);
var languageKind = context.CodeDocument.GetLanguageKind(context.StartAbsoluteIndex, rightAssociative: false);

// No point delegating if we're in a Razor context
if (languageKind == RazorLanguageKind.Razor)
{
return [];
}

var codeActions = await GetCodeActionsFromLanguageServerAsync(languageKind, documentContext, context, correlationId, cancellationToken).ConfigureAwait(false);
var codeActions = await GetCodeActionsFromLanguageServerAsync(languageKind, context, correlationId, cancellationToken).ConfigureAwait(false);
if (codeActions is not [_, ..])
{
return [];
Expand Down Expand Up @@ -265,14 +261,14 @@ private static async Task<ImmutableArray<RazorVSInternalCodeAction>> FilterCodeA
}

// Internal for testing
internal async Task<RazorVSInternalCodeAction[]> GetCodeActionsFromLanguageServerAsync(RazorLanguageKind languageKind, DocumentContext documentContext, RazorCodeActionContext context, Guid correlationId, CancellationToken cancellationToken)
internal Task<RazorVSInternalCodeAction[]> GetCodeActionsFromLanguageServerAsync(RazorLanguageKind languageKind, RazorCodeActionContext context, Guid correlationId, CancellationToken cancellationToken)
{
if (languageKind == RazorLanguageKind.CSharp)
{
// For C# we have to map the ranges to the generated document
if (!_documentMappingService.TryMapToGeneratedDocumentRange(context.CodeDocument.GetCSharpDocument(), context.Request.Range, out var projectedRange))
{
return [];
return SpecializedTasks.EmptyArray<RazorVSInternalCodeAction>();
}

var newContext = context.Request.Context;
Expand All @@ -288,25 +284,7 @@ internal async Task<RazorVSInternalCodeAction[]> GetCodeActionsFromLanguageServe
}

cancellationToken.ThrowIfCancellationRequested();

var delegatedParams = new DelegatedCodeActionParams()
{
HostDocumentVersion = documentContext.Snapshot.Version,
CodeActionParams = context.Request,
LanguageKind = languageKind,
CorrelationId = correlationId
};

try
{
return await _clientConnection.SendRequestAsync<DelegatedCodeActionParams, RazorVSInternalCodeAction[]>(CustomMessageNames.RazorProvideCodeActionsEndpoint, delegatedParams, cancellationToken).ConfigureAwait(false);
}
catch (RemoteInvocationException e)
{
_telemetryReporter?.ReportFault(e, "Error getting code actions from delegate language server for {languageKind}", languageKind);
_logger.LogError(e, $"Error getting code actions from delegate language server for {languageKind}");
return [];
}
return _delegatedCodeActionsProvider.GetDelegatedCodeActionsAsync(languageKind, context.Request, context.DocumentSnapshot.Version, correlationId, cancellationToken);
}

private async Task<ImmutableArray<RazorVSInternalCodeAction>> GetRazorCodeActionsAsync(RazorCodeActionContext context, CancellationToken cancellationToken)
Expand Down
Loading