-
Notifications
You must be signed in to change notification settings - Fork 198
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cohosted Code Actions Part 2: Re-layout in advance of moving down (#1…
…1083) Part of #10742 The next round of changes is to introduce the right abstractions so that the bulk of the code actions code can be moved to the Workspaces layer successfully. At least I think I got it all :D Reviewing commit at a time probably makes sense, but up to you.
- Loading branch information
Showing
99 changed files
with
1,630 additions
and
1,396 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 0 additions & 10 deletions
10
.../Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/CSharp/CSharpCodeActionResolver.cs
This file was deleted.
Oops, something went wrong.
333 changes: 14 additions & 319 deletions
333
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/CodeActionEndpoint.cs
Large diffs are not rendered by default.
Oops, something went wrong.
166 changes: 14 additions & 152 deletions
166
...or/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/CodeActionResolveEndpoint.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,177 +1,39 @@ | ||
// 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.Collections.Frozen; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Text.Json; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models; | ||
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts; | ||
using Microsoft.AspNetCore.Razor.PooledObjects; | ||
using Microsoft.CodeAnalysis.Razor.Logging; | ||
using Microsoft.CodeAnalysis.Razor.ProjectSystem; | ||
using Microsoft.CodeAnalysis.Razor.Protocol; | ||
using Microsoft.CodeAnalysis.Razor.CodeActions; | ||
using Microsoft.CodeAnalysis.Razor.Formatting; | ||
using Microsoft.VisualStudio.LanguageServer.Protocol; | ||
|
||
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions; | ||
|
||
[RazorLanguageServerEndpoint(Methods.CodeActionResolveName)] | ||
internal sealed class CodeActionResolveEndpoint( | ||
IEnumerable<IRazorCodeActionResolver> razorCodeActionResolvers, | ||
IEnumerable<CSharpCodeActionResolver> csharpCodeActionResolvers, | ||
IEnumerable<HtmlCodeActionResolver> htmlCodeActionResolvers, | ||
ILoggerFactory loggerFactory) : IRazorRequestHandler<CodeAction, CodeAction> | ||
ICodeActionResolveService codeActionResolveService, | ||
RazorLSPOptionsMonitor razorLSPOptionsMonitor) : IRazorRequestHandler<CodeAction, CodeAction> | ||
{ | ||
private readonly FrozenDictionary<string, IRazorCodeActionResolver> _razorCodeActionResolvers = CreateResolverMap(razorCodeActionResolvers); | ||
private readonly FrozenDictionary<string, BaseDelegatedCodeActionResolver> _csharpCodeActionResolvers = CreateResolverMap<BaseDelegatedCodeActionResolver>(csharpCodeActionResolvers); | ||
private readonly FrozenDictionary<string, BaseDelegatedCodeActionResolver> _htmlCodeActionResolvers = CreateResolverMap<BaseDelegatedCodeActionResolver>(htmlCodeActionResolvers); | ||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CodeActionResolveEndpoint>(); | ||
private readonly ICodeActionResolveService _codeActionResolveService = codeActionResolveService; | ||
private readonly RazorLSPOptionsMonitor _razorLSPOptionsMonitor = razorLSPOptionsMonitor; | ||
|
||
public bool MutatesSolutionState => false; | ||
|
||
public TextDocumentIdentifier GetTextDocumentIdentifier(CodeAction request) | ||
=> GetRazorCodeActionResolutionParams(request).TextDocument; | ||
=> _codeActionResolveService.GetRazorCodeActionResolutionParams(request).TextDocument; | ||
|
||
public async Task<CodeAction> HandleRequestAsync(CodeAction request, RazorRequestContext requestContext, CancellationToken cancellationToken) | ||
{ | ||
var resolutionParams = GetRazorCodeActionResolutionParams(request); | ||
var documentContext = requestContext.DocumentContext.AssumeNotNull(); | ||
|
||
var codeActionId = GetCodeActionId(resolutionParams); | ||
_logger.LogDebug($"Resolving workspace edit for action {codeActionId}."); | ||
|
||
// If it's a special "edit based code action" then the edit has been pre-computed and we | ||
// can extract the edit details and return to the client. This is only required for VSCode | ||
// as it does not support Command.Edit based code actions anymore. | ||
if (resolutionParams.Action == LanguageServerConstants.CodeActions.EditBasedCodeActionCommand) | ||
{ | ||
request.Edit = (resolutionParams.Data as JsonElement?)?.Deserialize<WorkspaceEdit>(); | ||
return request; | ||
} | ||
|
||
switch (resolutionParams.Language) | ||
{ | ||
case RazorLanguageKind.Razor: | ||
return await ResolveRazorCodeActionAsync( | ||
documentContext, | ||
request, | ||
resolutionParams, | ||
cancellationToken).ConfigureAwait(false); | ||
case RazorLanguageKind.CSharp: | ||
return await ResolveCSharpCodeActionAsync( | ||
documentContext, | ||
request, | ||
resolutionParams, | ||
cancellationToken).ConfigureAwait(false); | ||
case RazorLanguageKind.Html: | ||
return await ResolveHtmlCodeActionAsync( | ||
documentContext, | ||
request, | ||
resolutionParams, | ||
cancellationToken).ConfigureAwait(false); | ||
default: | ||
_logger.LogError($"Invalid CodeAction.Data.Language. Received {codeActionId}."); | ||
return request; | ||
} | ||
} | ||
|
||
private static RazorCodeActionResolutionParams GetRazorCodeActionResolutionParams(CodeAction request) | ||
{ | ||
if (request.Data is not JsonElement paramsObj) | ||
{ | ||
throw new InvalidOperationException($"Invalid CodeAction Received '{request.Title}'."); | ||
} | ||
|
||
var resolutionParams = paramsObj.Deserialize<RazorCodeActionResolutionParams>(); | ||
if (resolutionParams is null) | ||
{ | ||
throw new InvalidOperationException($"request.Data should be convertible to {nameof(RazorCodeActionResolutionParams)}"); | ||
} | ||
|
||
return resolutionParams; | ||
} | ||
|
||
private async Task<CodeAction> ResolveRazorCodeActionAsync( | ||
DocumentContext documentContext, | ||
CodeAction codeAction, | ||
RazorCodeActionResolutionParams resolutionParams, | ||
CancellationToken cancellationToken) | ||
{ | ||
if (!_razorCodeActionResolvers.TryGetValue(resolutionParams.Action, out var resolver)) | ||
{ | ||
var codeActionId = GetCodeActionId(resolutionParams); | ||
_logger.LogWarning($"No resolver registered for {codeActionId}"); | ||
Debug.Fail($"No resolver registered for {codeActionId}."); | ||
return codeAction; | ||
} | ||
|
||
if (resolutionParams.Data is not JsonElement data) | ||
{ | ||
return codeAction; | ||
} | ||
|
||
var edit = await resolver.ResolveAsync(documentContext, data, cancellationToken).ConfigureAwait(false); | ||
codeAction.Edit = edit; | ||
return codeAction; | ||
} | ||
|
||
private Task<CodeAction> ResolveCSharpCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) | ||
=> ResolveDelegatedCodeActionAsync(documentContext, _csharpCodeActionResolvers, codeAction, resolutionParams, cancellationToken); | ||
|
||
private Task<CodeAction> ResolveHtmlCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) | ||
=> ResolveDelegatedCodeActionAsync(documentContext, _htmlCodeActionResolvers, codeAction, resolutionParams, cancellationToken); | ||
|
||
private async Task<CodeAction> ResolveDelegatedCodeActionAsync(DocumentContext documentContext, FrozenDictionary<string, BaseDelegatedCodeActionResolver> resolvers, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) | ||
{ | ||
codeAction.Data = resolutionParams.Data; | ||
|
||
if (!resolvers.TryGetValue(resolutionParams.Action, out var resolver)) | ||
{ | ||
var codeActionId = GetCodeActionId(resolutionParams); | ||
_logger.LogWarning($"No resolver registered for {codeActionId}"); | ||
Debug.Fail($"No resolver registered for {codeActionId}."); | ||
return codeAction; | ||
} | ||
|
||
var resolvedCodeAction = await resolver.ResolveAsync(documentContext, codeAction, cancellationToken).ConfigureAwait(false); | ||
return resolvedCodeAction; | ||
} | ||
|
||
private static FrozenDictionary<string, T> CreateResolverMap<T>(IEnumerable<T> codeActionResolvers) | ||
where T : ICodeActionResolver | ||
{ | ||
using var _ = StringDictionaryPool<T>.GetPooledObject(out var resolverMap); | ||
|
||
foreach (var resolver in codeActionResolvers) | ||
var options = new RazorFormattingOptions | ||
{ | ||
if (resolverMap.ContainsKey(resolver.Action)) | ||
{ | ||
Debug.Fail($"Duplicate resolver action for {resolver.Action} of type {typeof(T)}."); | ||
} | ||
|
||
resolverMap[resolver.Action] = resolver; | ||
} | ||
|
||
return resolverMap.ToFrozenDictionary(); | ||
} | ||
|
||
private static string GetCodeActionId(RazorCodeActionResolutionParams resolutionParams) => | ||
$"`{resolutionParams.Language}.{resolutionParams.Action}`"; | ||
|
||
internal TestAccessor GetTestAccessor() => new(this); | ||
|
||
internal readonly struct TestAccessor(CodeActionResolveEndpoint instance) | ||
{ | ||
public Task<CodeAction> ResolveRazorCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) | ||
=> instance.ResolveRazorCodeActionAsync(documentContext, codeAction, resolutionParams, cancellationToken); | ||
TabSize = _razorLSPOptionsMonitor.CurrentValue.TabSize, | ||
InsertSpaces = _razorLSPOptionsMonitor.CurrentValue.InsertSpaces, | ||
CodeBlockBraceOnNextLine = _razorLSPOptionsMonitor.CurrentValue.CodeBlockBraceOnNextLine | ||
}; | ||
var documentContext = requestContext.DocumentContext.AssumeNotNull(); | ||
|
||
public Task<CodeAction> ResolveCSharpCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) | ||
=> instance.ResolveCSharpCodeActionAsync(documentContext, codeAction, resolutionParams, cancellationToken); | ||
return await _codeActionResolveService.ResolveCodeActionAsync(documentContext, request, options, cancellationToken).ConfigureAwait(false); | ||
|
||
public Task<CodeAction> ResolveHtmlCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) | ||
=> instance.ResolveCSharpCodeActionAsync(documentContext, codeAction, resolutionParams, cancellationToken); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
...src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/DelegatedCodeActionsProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// 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.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting; | ||
using Microsoft.AspNetCore.Razor.Telemetry; | ||
using Microsoft.CodeAnalysis.Razor.CodeActions; | ||
using Microsoft.CodeAnalysis.Razor.CodeActions.Models; | ||
using Microsoft.CodeAnalysis.Razor.Logging; | ||
using Microsoft.CodeAnalysis.Razor.Protocol; | ||
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions; | ||
using StreamJsonRpc; | ||
|
||
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions; | ||
|
||
internal sealed class DelegatedCodeActionsProvider( | ||
IClientConnection clientConnection, | ||
ITelemetryReporter telemetryReporter, | ||
ILoggerFactory loggerFactory) : IDelegatedCodeActionsProvider | ||
{ | ||
private readonly IClientConnection _clientConnection = clientConnection; | ||
private readonly ITelemetryReporter _telemetryReporter = telemetryReporter; | ||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<DelegatedCodeActionsProvider>(); | ||
|
||
public async Task<RazorVSInternalCodeAction[]> GetDelegatedCodeActionsAsync(RazorLanguageKind languageKind, VSCodeActionParams request, int hostDocumentVersion, Guid correlationId, CancellationToken cancellationToken) | ||
{ | ||
var delegatedParams = new DelegatedCodeActionParams() | ||
{ | ||
HostDocumentVersion = hostDocumentVersion, | ||
CodeActionParams = 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 []; | ||
} | ||
} | ||
} |
38 changes: 0 additions & 38 deletions
38
...crosoft.AspNetCore.Razor.LanguageServer/CodeActions/Html/DefaultHtmlCodeActionResolver.cs
This file was deleted.
Oops, something went wrong.
10 changes: 0 additions & 10 deletions
10
.../src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/Html/HtmlCodeActionResolver.cs
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.