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

Part of https://github.com/dotnet/razor/issues/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.
This commit is contained in:
David Wengier 2024-10-25 10:15:17 +11:00 коммит произвёл GitHub
Родитель af22ba0e1f 56ad7f7fa8
Коммит d371b1ef79
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
99 изменённых файлов: 1636 добавлений и 1402 удалений

Просмотреть файл

@ -2,7 +2,6 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
@ -12,11 +11,10 @@ using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -49,14 +47,8 @@ public class RazorCodeActionsBenchmark : RazorLanguageServerBenchmarkBase
public async Task SetupAsync()
{
CodeActionEndpoint = new CodeActionEndpoint(
documentMappingService: RazorLanguageServerHost.GetRequiredService<IDocumentMappingService>(),
razorCodeActionProviders: RazorLanguageServerHost.GetRequiredService<IEnumerable<IRazorCodeActionProvider>>(),
csharpCodeActionProviders: RazorLanguageServerHost.GetRequiredService<IEnumerable<ICSharpCodeActionProvider>>(),
htmlCodeActionProviders: RazorLanguageServerHost.GetRequiredService<IEnumerable<IHtmlCodeActionProvider>>(),
clientConnection: RazorLanguageServerHost.GetRequiredService<IClientConnection>(),
languageServerFeatureOptions: RazorLanguageServerHost.GetRequiredService<LanguageServerFeatureOptions>(),
loggerFactory: RazorLanguageServerHost.GetRequiredService<ILoggerFactory>(),
telemetryReporter: null);
codeActionsService: RazorLanguageServerHost.GetRequiredService<ICodeActionsService>(),
telemetryReporter: NoOpTelemetryReporter.Instance);
var projectRoot = Path.Combine(RepoRoot, "src", "Razor", "test", "testapps", "ComponentApp");
var projectFilePath = Path.Combine(projectRoot, "ComponentApp.csproj");

Просмотреть файл

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
internal abstract class CSharpCodeActionResolver(IClientConnection clientConnection) : BaseDelegatedCodeActionResolver(clientConnection)
{
}

Просмотреть файл

@ -2,121 +2,31 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
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.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.CodeActions;
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;
[RazorLanguageServerEndpoint(LspEndpointName)]
internal sealed class CodeActionEndpoint(
IDocumentMappingService documentMappingService,
IEnumerable<IRazorCodeActionProvider> razorCodeActionProviders,
IEnumerable<ICSharpCodeActionProvider> csharpCodeActionProviders,
IEnumerable<IHtmlCodeActionProvider> htmlCodeActionProviders,
IClientConnection clientConnection,
LanguageServerFeatureOptions languageServerFeatureOptions,
ILoggerFactory loggerFactory,
ITelemetryReporter? telemetryReporter)
ICodeActionsService codeActionsService,
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 ICodeActionsService _codeActionsService = codeActionsService;
private readonly ITelemetryReporter _telemetryReporter = telemetryReporter;
internal bool _supportsCodeActionResolve = false;
private readonly ImmutableHashSet<string> _allAvailableCodeActionNames = GetAllAvailableCodeActionNames();
public bool MutatesSolutionState { get; } = false;
public async Task<SumType<Command, CodeAction>[]?> HandleRequestAsync(VSCodeActionParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
{
var documentContext = requestContext.DocumentContext;
if (documentContext is null)
{
return null;
}
var razorCodeActionContext = await GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, cancellationToken).ConfigureAwait(false);
if (razorCodeActionContext is null)
{
return null;
}
cancellationToken.ThrowIfCancellationRequested();
var correlationId = Guid.NewGuid();
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);
cancellationToken.ThrowIfCancellationRequested();
using var commandsOrCodeActions = new PooledArrayBuilder<SumType<Command, CodeAction>>();
// Grouping the code actions causes VS to sort them into groups, rather than just alphabetically sorting them
// by title. The latter is bad for us because it can put "Remove <div>" at the top in some locales, and our fully
// qualify component code action at the bottom, depending on the users namespace.
ConvertCodeActionsToSumType(request.TextDocument, razorCodeActions, "A-Razor");
ConvertCodeActionsToSumType(request.TextDocument, delegatedCodeActions, "B-Delegated");
return commandsOrCodeActions.ToArray();
void ConvertCodeActionsToSumType(VSTextDocumentIdentifier textDocument, ImmutableArray<RazorVSInternalCodeAction> codeActions, string groupName)
{
// We must cast the RazorCodeAction into a platform compliant code action
// For VS (SupportsCodeActionResolve = true) this means just encapsulating the RazorCodeAction in the `CommandOrCodeAction` struct
// For VS Code (SupportsCodeActionResolve = false) we must convert it into a CodeAction or Command before encapsulating in the `CommandOrCodeAction` struct.
if (_supportsCodeActionResolve)
{
foreach (var action in codeActions)
{
// Make sure we honour the grouping that a delegated server may have created
action.Group = groupName + (action.Group ?? string.Empty);
commandsOrCodeActions.Add(action);
}
}
else
{
foreach (var action in codeActions)
{
commandsOrCodeActions.Add(action.AsVSCodeCommandOrCodeAction(textDocument));
}
}
}
}
public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, VSInternalClientCapabilities clientCapabilities)
{
_supportsCodeActionResolve = clientCapabilities.TextDocument?.CodeAction?.ResolveSupport is not null;
@ -133,238 +43,23 @@ internal sealed class CodeActionEndpoint(
};
}
// internal for testing
internal async Task<RazorCodeActionContext?> GenerateRazorCodeActionContextAsync(
VSCodeActionParams request,
IDocumentSnapshot documentSnapshot,
CancellationToken cancellationToken)
{
var codeDocument = await documentSnapshot.GetGeneratedOutputAsync(cancellationToken).ConfigureAwait(false);
if (codeDocument.IsUnsupported())
{
return null;
}
var sourceText = codeDocument.Source.Text;
// VS Provides `CodeActionParams.Context.SelectionRange` in addition to
// `CodeActionParams.Range`. The `SelectionRange` is relative to where the
// code action was invoked (ex. line 14, char 3) whereas the `Range` is
// always at the start of the line (ex. line 14, char 0). We want to utilize
// the relative positioning to ensure we provide code actions for the appropriate
// context.
//
// Note: VS Code doesn't provide a `SelectionRange`.
var vsCodeActionContext = request.Context;
if (vsCodeActionContext.SelectionRange != null)
{
request.Range = vsCodeActionContext.SelectionRange;
}
if (!sourceText.TryGetSourceLocation(request.Range.Start, out var startLocation))
{
return null;
}
if (!sourceText.TryGetSourceLocation(request.Range.End, out var endLocation))
{
endLocation = startLocation;
}
var context = new RazorCodeActionContext(
request,
documentSnapshot,
codeDocument,
startLocation,
endLocation,
sourceText,
_languageServerFeatureOptions.SupportsFileManipulation,
_supportsCodeActionResolve);
return context;
}
private async Task<ImmutableArray<RazorVSInternalCodeAction>> GetDelegatedCodeActionsAsync(DocumentContext documentContext, RazorCodeActionContext context, Guid correlationId, CancellationToken cancellationToken)
{
var languageKind = context.CodeDocument.GetLanguageKind(context.StartLocation.AbsoluteIndex, 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);
if (codeActions is not [_, ..])
{
return [];
}
IEnumerable<ICodeActionProvider> providers;
if (languageKind == RazorLanguageKind.CSharp)
{
codeActions = ExtractCSharpCodeActionNamesFromData(codeActions);
providers = _csharpCodeActionProviders;
}
else
{
providers = _htmlCodeActionProviders;
}
return await FilterCodeActionsAsync(context, codeActions.ToImmutableArray(), providers, cancellationToken).ConfigureAwait(false);
}
private RazorVSInternalCodeAction[] ExtractCSharpCodeActionNamesFromData(RazorVSInternalCodeAction[] codeActions)
{
using var actions = new PooledArrayBuilder<RazorVSInternalCodeAction>();
foreach (var codeAction in codeActions)
{
if (codeAction.Data is not JsonElement jsonData ||
!jsonData.TryGetProperty("CustomTags", out var value) ||
value.Deserialize<string[]>() is not [..] tags)
{
continue;
}
foreach (var tag in tags)
{
if (_allAvailableCodeActionNames.Contains(tag))
{
codeAction.Name = tag;
break;
}
}
if (string.IsNullOrEmpty(codeAction.Name))
{
continue;
}
actions.Add(codeAction);
}
return actions.ToArray();
}
private static async Task<ImmutableArray<RazorVSInternalCodeAction>> FilterCodeActionsAsync(
RazorCodeActionContext context,
ImmutableArray<RazorVSInternalCodeAction> codeActions,
IEnumerable<ICodeActionProvider> providers,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var tasks = new PooledArrayBuilder<Task<ImmutableArray<RazorVSInternalCodeAction>>>();
foreach (var provider in providers)
{
tasks.Add(provider.ProvideAsync(context, codeActions, cancellationToken));
}
return await ConsolidateCodeActionsFromProvidersAsync(tasks.ToImmutable(), cancellationToken).ConfigureAwait(false);
}
// Internal for testing
internal async Task<RazorVSInternalCodeAction[]> GetCodeActionsFromLanguageServerAsync(RazorLanguageKind languageKind, DocumentContext documentContext, 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 [];
}
var newContext = context.Request.Context;
if (context.Request.Context is VSInternalCodeActionContext { SelectionRange: not null } vsContext &&
_documentMappingService.TryMapToGeneratedDocumentRange(context.CodeDocument.GetCSharpDocument(), vsContext.SelectionRange, out var selectionRange))
{
vsContext.SelectionRange = selectionRange;
newContext = vsContext;
}
context.Request.Range = projectedRange;
context.Request.Context = newContext;
}
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 [];
}
}
private async Task<ImmutableArray<RazorVSInternalCodeAction>> GetRazorCodeActionsAsync(RazorCodeActionContext context, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var tasks = new PooledArrayBuilder<Task<ImmutableArray<RazorVSInternalCodeAction>>>();
foreach (var provider in _razorCodeActionProviders)
{
tasks.Add(provider.ProvideAsync(context, cancellationToken));
}
return await ConsolidateCodeActionsFromProvidersAsync(tasks.ToImmutable(), cancellationToken).ConfigureAwait(false);
}
private static async Task<ImmutableArray<RazorVSInternalCodeAction>> ConsolidateCodeActionsFromProvidersAsync(
ImmutableArray<Task<ImmutableArray<RazorVSInternalCodeAction>>> tasks,
CancellationToken cancellationToken)
{
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
using var codeActions = new PooledArrayBuilder<RazorVSInternalCodeAction>();
cancellationToken.ThrowIfCancellationRequested();
foreach (var result in results)
{
codeActions.AddRange(result);
}
return codeActions.ToImmutable();
}
private static ImmutableHashSet<string> GetAllAvailableCodeActionNames()
{
using var _ = ArrayBuilderPool<string>.GetPooledObject(out var availableCodeActionNames);
var refactoringProviderNames = typeof(RazorPredefinedCodeRefactoringProviderNames)
.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string))
.Select(property => property.GetValue(null) as string)
.WhereNotNull();
var codeFixProviderNames = typeof(RazorPredefinedCodeFixProviderNames)
.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string))
.Select(property => property.GetValue(null) as string)
.WhereNotNull();
availableCodeActionNames.AddRange(refactoringProviderNames);
availableCodeActionNames.AddRange(codeFixProviderNames);
availableCodeActionNames.Add(LanguageServerConstants.CodeActions.CodeActionFromVSCode);
return availableCodeActionNames.ToImmutableHashSet();
}
public TextDocumentIdentifier GetTextDocumentIdentifier(VSCodeActionParams request)
{
return request.TextDocument;
}
public async Task<SumType<Command, CodeAction>[]?> HandleRequestAsync(VSCodeActionParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
{
var documentContext = requestContext.DocumentContext;
if (documentContext is null)
{
return null;
}
var correlationId = Guid.NewGuid();
using var __ = _telemetryReporter.TrackLspRequest(LspEndpointName, LanguageServerConstants.RazorLanguageServerName, correlationId);
cancellationToken.ThrowIfCancellationRequested();
return await _codeActionsService.GetCodeActionsAsync(request, documentContext, _supportsCodeActionResolve, correlationId, cancellationToken).ConfigureAwait(false);
}
}

Просмотреть файл

@ -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 options = new RazorFormattingOptions
{
TabSize = _razorLSPOptionsMonitor.CurrentValue.TabSize,
InsertSpaces = _razorLSPOptionsMonitor.CurrentValue.InsertSpaces,
CodeBlockBraceOnNextLine = _razorLSPOptionsMonitor.CurrentValue.CodeBlockBraceOnNextLine
};
var documentContext = requestContext.DocumentContext.AssumeNotNull();
var codeActionId = GetCodeActionId(resolutionParams);
_logger.LogDebug($"Resolving workspace edit for action {codeActionId}.");
return await _codeActionResolveService.ResolveCodeActionAsync(documentContext, request, options, cancellationToken).ConfigureAwait(false);
// 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)
{
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);
public Task<CodeAction> ResolveCSharpCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken)
=> instance.ResolveCSharpCodeActionAsync(documentContext, codeAction, resolutionParams, cancellationToken);
public Task<CodeAction> ResolveHtmlCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken)
=> instance.ResolveCSharpCodeActionAsync(documentContext, codeAction, resolutionParams, cancellationToken);
}
}

Просмотреть файл

@ -4,26 +4,22 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
internal abstract class BaseDelegatedCodeActionResolver(IClientConnection clientConnection) : ICodeActionResolver
internal sealed class DelegatedCodeActionResolver(IClientConnection clientConnection) : IDelegatedCodeActionResolver
{
protected readonly IClientConnection ClientConnection = clientConnection;
private readonly IClientConnection _clientConnection = clientConnection;
public abstract string Action { get; }
public abstract Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken);
protected async Task<CodeAction?> ResolveCodeActionWithServerAsync(TextDocumentIdentifier razorFileIdentifier, int hostDocumentVersion, RazorLanguageKind languageKind, CodeAction codeAction, CancellationToken cancellationToken)
public async Task<CodeAction?> ResolveCodeActionAsync(TextDocumentIdentifier razorFileIdentifier, int hostDocumentVersion, RazorLanguageKind languageKind, CodeAction codeAction, CancellationToken cancellationToken)
{
var resolveCodeActionParams = new RazorResolveCodeActionParams(razorFileIdentifier, hostDocumentVersion, languageKind, codeAction);
var resolvedCodeAction = await ClientConnection.SendRequestAsync<RazorResolveCodeActionParams, CodeAction?>(
var resolvedCodeAction = await _clientConnection.SendRequestAsync<RazorResolveCodeActionParams, CodeAction?>(
CustomMessageNames.RazorResolveCodeActionsEndpoint,
resolveCodeActionParams,
cancellationToken).ConfigureAwait(false);

Просмотреть файл

@ -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 [];
}
}
}

Просмотреть файл

@ -1,38 +0,0 @@
// 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.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
internal sealed class DefaultHtmlCodeActionResolver(
IClientConnection clientConnection,
IEditMappingService editMappingService) : HtmlCodeActionResolver(clientConnection)
{
private readonly IEditMappingService _editMappingService = editMappingService;
public override string Action => LanguageServerConstants.CodeActions.Default;
public async override Task<CodeAction> ResolveAsync(
DocumentContext documentContext,
CodeAction codeAction,
CancellationToken cancellationToken)
{
var resolvedCodeAction = await ResolveCodeActionWithServerAsync(documentContext.GetTextDocumentIdentifier(), documentContext.Snapshot.Version, RazorLanguageKind.Html, codeAction, cancellationToken).ConfigureAwait(false);
if (resolvedCodeAction?.Edit?.DocumentChanges is null)
{
// Unable to resolve code action with server, return original code action
return codeAction;
}
await DefaultHtmlCodeActionProvider.RemapAndFixHtmlCodeActionEditAsync(_editMappingService, documentContext.Snapshot, resolvedCodeAction, cancellationToken).ConfigureAwait(false);
return resolvedCodeAction;
}
}

Просмотреть файл

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
internal abstract class HtmlCodeActionResolver(IClientConnection clientConnection) : BaseDelegatedCodeActionResolver(clientConnection)
{
}

Просмотреть файл

@ -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.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
internal sealed class RoslynCodeActionHelpers(IClientConnection clientConnection) : IRoslynCodeActionHelpers
{
private readonly IClientConnection _clientConnection = clientConnection;
public Task<string?> GetFormattedNewFileContentsAsync(string projectFilePath, Uri csharpFileUri, string newFileContent, CancellationToken cancellationToken)
{
var parameters = new FormatNewFileParams()
{
Project = new TextDocumentIdentifier
{
Uri = new Uri(projectFilePath, UriKind.Absolute)
},
Document = new TextDocumentIdentifier
{
Uri = csharpFileUri
},
Contents = newFileContent
};
return _clientConnection.SendRequestAsync<FormatNewFileParams, string?>(CustomMessageNames.RazorFormatNewFileEndpointName, parameters, cancellationToken);
}
public Task<TextEdit[]?> GetSimplifiedTextEditsAsync(Uri codeBehindUri, TextEdit edit, bool requiresVirtualDocument, CancellationToken cancellationToken)
{
var delegatedParams = new DelegatedSimplifyMethodParams(
new TextDocumentIdentifierAndVersion(new TextDocumentIdentifier() { Uri = codeBehindUri }, 1),
requiresVirtualDocument,
edit);
return _clientConnection.SendRequestAsync<DelegatedSimplifyMethodParams, TextEdit[]?>(
CustomMessageNames.RazorSimplifyMethodEndpointName,
delegatedParams,
cancellationToken);
}
}

Просмотреть файл

@ -3,7 +3,6 @@
using System;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
using Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
@ -20,6 +19,8 @@ using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.AspNetCore.Razor.LanguageServer.Semantic;
using Microsoft.AspNetCore.Razor.LanguageServer.SpellCheck;
using Microsoft.AspNetCore.Razor.ProjectEngineHost;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Diagnostics;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
@ -137,17 +138,23 @@ internal static class IServiceCollectionExtensions
services.AddHandlerWithCapabilities<CodeActionEndpoint>();
services.AddHandler<CodeActionResolveEndpoint>();
services.AddSingleton<ICodeActionsService, CodeActionsService>();
services.AddSingleton<ICodeActionResolveService, CodeActionResolveService>();
services.AddSingleton<IDelegatedCodeActionsProvider, DelegatedCodeActionsProvider>();
services.AddSingleton<IDelegatedCodeActionResolver, DelegatedCodeActionResolver>();
services.AddSingleton<IRoslynCodeActionHelpers, RoslynCodeActionHelpers>();
// CSharp Code actions
services.AddSingleton<ICSharpCodeActionProvider, TypeAccessibilityCodeActionProvider>();
services.AddSingleton<ICSharpCodeActionProvider, DefaultCSharpCodeActionProvider>();
services.AddSingleton<CSharpCodeActionResolver, DefaultCSharpCodeActionResolver>();
services.AddSingleton<CSharpCodeActionResolver, UnformattedRemappingCSharpCodeActionResolver>();
services.AddSingleton<ICSharpCodeActionProvider, CSharpCodeActionProvider>();
services.AddSingleton<ICSharpCodeActionResolver, CSharpCodeActionResolver>();
services.AddSingleton<ICSharpCodeActionResolver, UnformattedRemappingCSharpCodeActionResolver>();
// Razor Code actions
services.AddSingleton<IRazorCodeActionProvider, ExtractToCodeBehindCodeActionProvider>();
services.AddSingleton<IRazorCodeActionResolver, ExtractToCodeBehindCodeActionResolver>();
services.AddSingleton<IRazorCodeActionProvider, ExtractToComponentCodeActionProvider>();
services.AddSingleton<IRazorCodeActionResolver ,ExtractToComponentCodeActionResolver>();
services.AddSingleton<IRazorCodeActionResolver, ExtractToComponentCodeActionResolver>();
services.AddSingleton<IRazorCodeActionProvider, ComponentAccessibilityCodeActionProvider>();
services.AddSingleton<IRazorCodeActionResolver, CreateComponentCodeActionResolver>();
services.AddSingleton<IRazorCodeActionResolver, AddUsingsCodeActionResolver>();
@ -155,8 +162,8 @@ internal static class IServiceCollectionExtensions
services.AddSingleton<IRazorCodeActionResolver, GenerateMethodCodeActionResolver>();
// Html Code actions
services.AddSingleton<IHtmlCodeActionProvider, DefaultHtmlCodeActionProvider>();
services.AddSingleton<HtmlCodeActionResolver, DefaultHtmlCodeActionResolver>();
services.AddSingleton<IHtmlCodeActionProvider, HtmlCodeActionProvider>();
services.AddSingleton<IHtmlCodeActionResolver, HtmlCodeActionResolver>();
}
public static void AddTextDocumentServices(this IServiceCollection services, LanguageServerFeatureOptions featureOptions)

Просмотреть файл

@ -81,22 +81,4 @@ internal sealed class HtmlFormatter(
var sourceText = await documentSnapshot.GetTextAsync(cancellationToken).ConfigureAwait(false);
return result.Edits.SelectAsArray(sourceText.GetTextChange);
}
/// <summary>
/// Sometimes the Html language server will send back an edit that contains a tilde, because the generated
/// document we send them has lots of tildes. In those cases, we need to do some extra work to compute the
/// minimal text edits
/// </summary>
// Internal for testing
public static TextEdit[] FixHtmlTextEdits(SourceText htmlSourceText, TextEdit[] edits)
{
// Avoid computing a minimal diff if we don't need to
if (!edits.Any(static e => e.NewText.Contains("~")))
return edits;
var changes = edits.SelectAsArray(htmlSourceText.GetTextChange);
var fixedChanges = htmlSourceText.MinimizeTextChanges(changes);
return [.. fixedChanges.Select(htmlSourceText.GetTextEdit)];
}
}

Просмотреть файл

@ -123,24 +123,12 @@
<data name="Changes" xml:space="preserve">
<value>Changes:</value>
</data>
<data name="Create_Component_FromTag_Title" xml:space="preserve">
<value>Create component from tag</value>
</data>
<data name="Document_Not_Found" xml:space="preserve">
<value>Document {0} was not found.</value>
</data>
<data name="ExtractTo_CodeBehind_Title" xml:space="preserve">
<value>Extract block to code behind</value>
</data>
<data name="File_Externally_Modified" xml:space="preserve">
<value>File was externally modified: {0}</value>
</data>
<data name="Generate_Async_Event_Handler_Title" xml:space="preserve">
<value>Generate Async Event Handler '{0}'</value>
</data>
<data name="Generate_Event_Handler_Title" xml:space="preserve">
<value>Generate Event Handler '{0}'</value>
</data>
<data name="ReTrigger_Completions_Title" xml:space="preserve">
<value>"Re-trigger completions..."</value>
</data>
@ -153,7 +141,4 @@
<data name="Statement" xml:space="preserve">
<value>statement</value>
</data>
<data name="ExtractTo_Component_Title" xml:space="preserve">
<value>Extract element to new component</value>
</data>
</root>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Změny:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Vytvořit komponentu ze značky</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">Dokument {0} nebyl nalezen.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Extrahovat blok do kódu na pozadí</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Extrahovat element do nové komponenty</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">Došlo k externí úpravě souboru: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Generovat asynchronní obslužnou rutinu události {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Generovat obslužnou rutinu události {0}</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">Aktivovat znovu dokončení…</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Änderungen:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Komponente aus Tag erstellen</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">Dokument „{0}“ wurde nicht gefunden.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Block auf CodeBehind extrahieren</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Element in neue Komponente extrahieren</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">Datei wurde extern modifiziert: {0}.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Asynchronen Ereignishandler "{0}" generieren</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Ereignishandler "{0}" generieren</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"Abschlüsse erneut auslösen..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Cambios:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Crear un componente a partir de la etiqueta</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">No se encontró el documento {0}.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Extraer el bloque al código subyacente</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Extraer elemento a componente nuevo</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">El archivo se modificó externamente: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Generar controlador de eventos asincrónicos ''{0}''</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Generar controlador de eventos ''{0}''</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"Volver a desencadenar las finalizaciones..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Modifications :</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Créer un composant à partir de la balise</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">Le document {0} est introuvable.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Extraire le bloc vers le code-behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Extraire lélément vers un nouveau composant</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">Le fichier a été modifié en externe : {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Générer le gestionnaire dévénements asynchrone « {0} »</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Générer le gestionnaire dévénements '{0}'</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">« Déclencher à nouveau les complétions... »</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Modifiche:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Crea componente da tag</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">Impossibile trovare il documento{0}.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Estrai il blocco in code-behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Estrarre elemento nel nuovo componente</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">Il file è stato modificato esternamente: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Genera gestore dell'evento '{0}' asincrono</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Genera gestore dell'evento '{0}'</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"Riattiva i completamenti..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">変更:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">タグからコンポーネントを作成する</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">ドキュメント {0}が見つかりませんでした。</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">ブロック抽出から分離コード</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">要素を新しいコンポーネントに抽出する</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">ファイルが外部で変更されました: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">非同期イベント ハンドラー '{0}' の生成</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">イベント ハンドラー '{0}' の生成</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"再トリガーの完了..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">변경 내용:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">태그에서 구성 요소 만들기</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">문서 {0}을(를) 찾을 수 없습니다.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">코드 숨김에 블록 추출</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">새 구성 요소에 요소 추출</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">{0}의 파일이 외부에서 수정되었습니다.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">비동기 이벤트 처리기 '{0}' 생성</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">이벤트 처리기 '{0}' 생성</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"완료된 항목 다시 트리거"</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Zmiany:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Utwórz składnik z tagu</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">Nie znaleziono {0} dokumentu.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Wyodrębnij blok do kodu znajdującego się poza</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Wyodrębnij element do nowego składnika</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">Plik został zmodyfikowany na zewnątrz: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Generuj asynchroniczny program obsługi zdarzeń „{0}”</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Generuj obsługę zdarzeń „{0}”</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"Ponownie wyzwalaj uzupełniania..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Alterações:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Criar componente a partir da marca</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">O documento {0} não foi encontrado.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Extrair o bloco para codificar atrás</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Extrair elemento para o novo componente</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">O arquivo foi modificado externamente: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Gerar Manipulador de Eventos Assíncronos '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Gerar manipulador de eventos '{0}'</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"Disparar conclusões novamente..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Изменения:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Создание компонента из тега</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">Документ {0} не найден.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Извлечь блок в код программной части</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Извлечь элемент в новый компонент</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">Файл был изменен извне: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">Создать обработчик асинхронных событий "{0}"</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">Создать обработчик событий "{0}"</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"Повторный запуск завершений..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">Değişiklikler:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">Etiketten bileşen oluştur</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">Belge {0} bulunamadı.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">Bloğu arkadaki koda ayıkla</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">Öğeyi yeni bileşene ayıklayın</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">Dosya dışarıdan değiştirildi: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">'{0}' Asenkron Olay İşleyicisini Oluştur</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">'{0}' Olay İşleyicisi Oluştur</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"Tamamlamaları yeniden tetikleyin..."</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">更改:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">从标记创建组件</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">找不到文档 {0}。</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">将块提取到代码隐藏中</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">将元素提取到新组件</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">已从外部修改了文件: {0}。</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">生成异步事件处理程序“{0}”</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">生成事件处理程序 "{0}"</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">“重新触发完成…”</target>

Просмотреть файл

@ -12,41 +12,16 @@
<target state="translated">變更:</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="translated">從標籤建立元件</target>
<note />
</trans-unit>
<trans-unit id="Document_Not_Found">
<source>Document {0} was not found.</source>
<target state="translated">找不到文件 {0}。</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="translated">擷取區塊以在後方編碼</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="translated">將元素擷取至新元件</target>
<note />
</trans-unit>
<trans-unit id="File_Externally_Modified">
<source>File was externally modified: {0}</source>
<target state="translated">已在外部修改檔案: {0}</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="translated">產生非同步事件處理常式 '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="translated">產生事件處理常式 '{0}'</target>
<note />
</trans-unit>
<trans-unit id="ReTrigger_Completions_Title">
<source>"Re-trigger completions..."</source>
<target state="translated">"重新觸發完成..."</target>

Просмотреть файл

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Text;
@ -122,7 +123,7 @@ internal class WrapWithTagEndpoint(IClientConnection clientConnection, ILoggerFa
if (htmlResponse.TextEdits is not null)
{
var htmlSourceText = await documentContext.GetHtmlSourceTextAsync(cancellationToken).ConfigureAwait(false);
htmlResponse.TextEdits = HtmlFormatter.FixHtmlTextEdits(htmlSourceText, htmlResponse.TextEdits);
htmlResponse.TextEdits = FormattingUtilities.FixHtmlTextEdits(htmlSourceText, htmlResponse.TextEdits);
}
return htmlResponse;

Просмотреть файл

@ -1,7 +1,6 @@
// 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.Generic;
using System.Collections.Immutable;
using System.Linq;
@ -10,15 +9,15 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Workspaces;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class DefaultCSharpCodeActionProvider(LanguageServerFeatureOptions languageServerFeatureOptions) : ICSharpCodeActionProvider
internal sealed class CSharpCodeActionProvider(LanguageServerFeatureOptions languageServerFeatureOptions) : ICSharpCodeActionProvider
{
// Internal for testing
internal static readonly HashSet<string> SupportedDefaultCodeActionNames =
@ -67,7 +66,7 @@ internal sealed class DefaultCSharpCodeActionProvider(LanguageServerFeatureOptio
}
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

Просмотреть файл

@ -1,33 +1,32 @@
// 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.Generic;
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;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class DefaultCSharpCodeActionResolver(
IClientConnection clientConnection,
IRazorFormattingService razorFormattingService) : CSharpCodeActionResolver(clientConnection)
internal sealed class CSharpCodeActionResolver(
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

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface ICSharpCodeActionProvider : ICodeActionProvider
{

Просмотреть файл

@ -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.CodeAnalysis.Razor.CodeActions;
internal interface ICSharpCodeActionResolver : ICodeActionResolver
{
Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken);
}

Просмотреть файл

@ -11,16 +11,21 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using LspDiagnostic = Microsoft.VisualStudio.LanguageServer.Protocol.Diagnostic;
using LspDiagnosticSeverity = Microsoft.VisualStudio.LanguageServer.Protocol.DiagnosticSeverity;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
internal sealed class TypeAccessibilityCodeActionProvider : ICSharpCodeActionProvider
{
@ -67,7 +72,7 @@ internal sealed class TypeAccessibilityCodeActionProvider : ICSharpCodeActionPro
ImmutableArray<RazorVSInternalCodeAction> codeActions)
{
var diagnostics = context.Request.Context.Diagnostics.Where(diagnostic =>
diagnostic is { Severity: DiagnosticSeverity.Error, Code: { } code } &&
diagnostic is { Severity: LspDiagnosticSeverity.Error, Code: { } code } &&
code.TryGetSecond(out var str) &&
s_supportedDiagnostics.Any(d => str.Equals(d, StringComparison.OrdinalIgnoreCase)));
@ -215,7 +220,7 @@ internal sealed class TypeAccessibilityCodeActionProvider : ICSharpCodeActionPro
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.");
@ -256,7 +261,7 @@ internal sealed class TypeAccessibilityCodeActionProvider : ICSharpCodeActionPro
private static RazorVSInternalCodeAction CreateFQNCodeAction(
RazorCodeActionContext context,
Diagnostic fqnDiagnostic,
LspDiagnostic fqnDiagnostic,
RazorVSInternalCodeAction nonFQNCodeAction,
string fullyQualifiedName)
{

Просмотреть файл

@ -1,39 +1,38 @@
// 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.Diagnostics;
using System.Linq;
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;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
/// <summary>
/// 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

Просмотреть файл

@ -0,0 +1,188 @@
// 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.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class CodeActionResolveService(
IEnumerable<IRazorCodeActionResolver> razorCodeActionResolvers,
IEnumerable<ICSharpCodeActionResolver> csharpCodeActionResolvers,
IEnumerable<IHtmlCodeActionResolver> htmlCodeActionResolvers,
ILoggerFactory loggerFactory) : ICodeActionResolveService
{
private readonly FrozenDictionary<string, IRazorCodeActionResolver> _razorCodeActionResolvers = CreateResolverMap(razorCodeActionResolvers);
private readonly FrozenDictionary<string, ICSharpCodeActionResolver> _csharpCodeActionResolvers = CreateResolverMap(csharpCodeActionResolvers);
private readonly FrozenDictionary<string, IHtmlCodeActionResolver> _htmlCodeActionResolvers = CreateResolverMap(htmlCodeActionResolvers);
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CodeActionResolveService>();
public async Task<CodeAction> ResolveCodeActionAsync(DocumentContext documentContext, CodeAction request, RazorFormattingOptions options, CancellationToken cancellationToken)
{
var resolutionParams = GetRazorCodeActionResolutionParams(request);
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;
}
request.Data = resolutionParams.Data;
switch (resolutionParams.Language)
{
case RazorLanguageKind.Razor:
return await ResolveRazorCodeActionAsync(
documentContext,
request,
resolutionParams,
options,
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;
}
}
public 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,
RazorFormattingOptions options,
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, options, cancellationToken).ConfigureAwait(false);
codeAction.Edit = edit;
return codeAction;
}
private async Task<CodeAction> ResolveCSharpCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken)
{
if (TryGetResolver(resolutionParams, _csharpCodeActionResolvers, out var resolver))
{
return await resolver.ResolveAsync(documentContext, codeAction, cancellationToken).ConfigureAwait(false);
}
return codeAction;
}
private async Task<CodeAction> ResolveHtmlCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken)
{
if (TryGetResolver(resolutionParams, _htmlCodeActionResolvers, out var resolver))
{
return await resolver.ResolveAsync(documentContext, codeAction, cancellationToken).ConfigureAwait(false);
}
return codeAction;
}
private bool TryGetResolver<TResolver>(RazorCodeActionResolutionParams resolutionParams, FrozenDictionary<string, TResolver> resolvers, [NotNullWhen(true)] out TResolver? resolver)
where TResolver : ICodeActionResolver
{
if (!resolvers.TryGetValue(resolutionParams.Action, out resolver))
{
var codeActionId = GetCodeActionId(resolutionParams);
_logger.LogWarning($"No resolver registered for {codeActionId}");
Debug.Fail($"No resolver registered for {codeActionId}.");
return false;
}
return resolver is not null;
}
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)
{
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(CodeActionResolveService instance)
{
public Task<CodeAction> ResolveRazorCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, RazorFormattingOptions options, CancellationToken cancellationToken)
=> instance.ResolveRazorCodeActionAsync(documentContext, codeAction, resolutionParams, options, cancellationToken);
public Task<CodeAction> ResolveCSharpCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken)
=> instance.ResolveCSharpCodeActionAsync(documentContext, codeAction, resolutionParams, cancellationToken);
public Task<CodeAction> ResolveHtmlCodeActionAsync(DocumentContext documentContext, CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken)
=> instance.ResolveCSharpCodeActionAsync(documentContext, codeAction, resolutionParams, cancellationToken);
}
}

Просмотреть файл

@ -0,0 +1,313 @@
// 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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class CodeActionsService(
IDocumentMappingService documentMappingService,
IEnumerable<IRazorCodeActionProvider> razorCodeActionProviders,
IEnumerable<ICSharpCodeActionProvider> csharpCodeActionProviders,
IEnumerable<IHtmlCodeActionProvider> htmlCodeActionProviders,
IDelegatedCodeActionsProvider delegatedCodeActionsProvider,
LanguageServerFeatureOptions languageServerFeatureOptions) : ICodeActionsService
{
private static readonly ImmutableHashSet<string> s_allAvailableCodeActionNames = GetAllAvailableCodeActionNames();
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 IDelegatedCodeActionsProvider _delegatedCodeActionsProvider = delegatedCodeActionsProvider;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
public async Task<SumType<Command, CodeAction>[]?> GetCodeActionsAsync(VSCodeActionParams request, DocumentContext documentContext, bool supportsCodeActionResolve, Guid correlationId, CancellationToken cancellationToken)
{
var razorCodeActionContext = await GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, supportsCodeActionResolve, cancellationToken).ConfigureAwait(false);
if (razorCodeActionContext is null)
{
return null;
}
var razorCodeActions = await GetRazorCodeActionsAsync(razorCodeActionContext, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
var delegatedCodeActions = await GetDelegatedCodeActionsAsync(razorCodeActionContext, correlationId, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
using var commandsOrCodeActions = new PooledArrayBuilder<SumType<Command, CodeAction>>();
// Grouping the code actions causes VS to sort them into groups, rather than just alphabetically sorting them
// by title. The latter is bad for us because it can put "Remove <div>" at the top in some locales, and our fully
// qualify component code action at the bottom, depending on the users namespace.
ConvertCodeActionsToSumType(request.TextDocument, razorCodeActions, "A-Razor");
ConvertCodeActionsToSumType(request.TextDocument, delegatedCodeActions, "B-Delegated");
return commandsOrCodeActions.ToArray();
void ConvertCodeActionsToSumType(VSTextDocumentIdentifier textDocument, ImmutableArray<RazorVSInternalCodeAction> codeActions, string groupName)
{
// We must cast the RazorCodeAction into a platform compliant code action
// For VS (SupportsCodeActionResolve = true) this means just encapsulating the RazorCodeAction in the `CommandOrCodeAction` struct
// For VS Code (SupportsCodeActionResolve = false) we must convert it into a CodeAction or Command before encapsulating in the `CommandOrCodeAction` struct.
if (supportsCodeActionResolve)
{
foreach (var action in codeActions)
{
// Make sure we honour the grouping that a delegated server may have created
action.Group = groupName + (action.Group ?? string.Empty);
commandsOrCodeActions.Add(action);
}
}
else
{
foreach (var action in codeActions)
{
commandsOrCodeActions.Add(action.AsVSCodeCommandOrCodeAction(textDocument));
}
}
}
}
private async Task<RazorCodeActionContext?> GenerateRazorCodeActionContextAsync(
VSCodeActionParams request,
IDocumentSnapshot documentSnapshot,
bool supportsCodeActionResolve,
CancellationToken cancellationToken)
{
var codeDocument = await documentSnapshot.GetGeneratedOutputAsync(cancellationToken).ConfigureAwait(false);
if (codeDocument.IsUnsupported())
{
return null;
}
var sourceText = codeDocument.Source.Text;
// VS Provides `CodeActionParams.Context.SelectionRange` in addition to
// `CodeActionParams.Range`. The `SelectionRange` is relative to where the
// code action was invoked (ex. line 14, char 3) whereas the `Range` is
// always at the start of the line (ex. line 14, char 0). We want to utilize
// the relative positioning to ensure we provide code actions for the appropriate
// context.
//
// Note: VS Code doesn't provide a `SelectionRange`.
var vsCodeActionContext = request.Context;
if (vsCodeActionContext.SelectionRange != null)
{
request.Range = vsCodeActionContext.SelectionRange;
}
if (!sourceText.TryGetAbsoluteIndex(request.Range.Start, out var startLocation))
{
return null;
}
if (!sourceText.TryGetAbsoluteIndex(request.Range.End, out var endLocation))
{
endLocation = startLocation;
}
var context = new RazorCodeActionContext(
request,
documentSnapshot,
codeDocument,
startLocation,
endLocation,
sourceText,
_languageServerFeatureOptions.SupportsFileManipulation,
supportsCodeActionResolve);
return context;
}
private async Task<ImmutableArray<RazorVSInternalCodeAction>> GetDelegatedCodeActionsAsync(RazorCodeActionContext context, Guid correlationId, CancellationToken cancellationToken)
{
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, context, correlationId, cancellationToken).ConfigureAwait(false);
if (codeActions is not [_, ..])
{
return [];
}
IEnumerable<ICodeActionProvider> providers;
if (languageKind == RazorLanguageKind.CSharp)
{
codeActions = ExtractCSharpCodeActionNamesFromData(codeActions);
providers = _csharpCodeActionProviders;
}
else
{
providers = _htmlCodeActionProviders;
}
return await FilterCodeActionsAsync(context, codeActions.ToImmutableArray(), providers, cancellationToken).ConfigureAwait(false);
}
private RazorVSInternalCodeAction[] ExtractCSharpCodeActionNamesFromData(RazorVSInternalCodeAction[] codeActions)
{
using var actions = new PooledArrayBuilder<RazorVSInternalCodeAction>();
foreach (var codeAction in codeActions)
{
if (codeAction.Data is not JsonElement jsonData ||
!jsonData.TryGetProperty("CustomTags", out var value) ||
value.Deserialize<string[]>() is not [..] tags)
{
continue;
}
foreach (var tag in tags)
{
if (s_allAvailableCodeActionNames.Contains(tag))
{
codeAction.Name = tag;
break;
}
}
if (string.IsNullOrEmpty(codeAction.Name))
{
continue;
}
actions.Add(codeAction);
}
return actions.ToArray();
}
private static async Task<ImmutableArray<RazorVSInternalCodeAction>> FilterCodeActionsAsync(
RazorCodeActionContext context,
ImmutableArray<RazorVSInternalCodeAction> codeActions,
IEnumerable<ICodeActionProvider> providers,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var tasks = new PooledArrayBuilder<Task<ImmutableArray<RazorVSInternalCodeAction>>>();
foreach (var provider in providers)
{
tasks.Add(provider.ProvideAsync(context, codeActions, cancellationToken));
}
return await ConsolidateCodeActionsFromProvidersAsync(tasks.ToImmutable(), cancellationToken).ConfigureAwait(false);
}
private 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 SpecializedTasks.EmptyArray<RazorVSInternalCodeAction>();
}
var newContext = context.Request.Context;
if (context.Request.Context is VSInternalCodeActionContext { SelectionRange: not null } vsContext &&
_documentMappingService.TryMapToGeneratedDocumentRange(context.CodeDocument.GetCSharpDocument(), vsContext.SelectionRange, out var selectionRange))
{
vsContext.SelectionRange = selectionRange;
newContext = vsContext;
}
context.Request.Range = projectedRange;
context.Request.Context = newContext;
}
cancellationToken.ThrowIfCancellationRequested();
return _delegatedCodeActionsProvider.GetDelegatedCodeActionsAsync(languageKind, context.Request, context.DocumentSnapshot.Version, correlationId, cancellationToken);
}
private async Task<ImmutableArray<RazorVSInternalCodeAction>> GetRazorCodeActionsAsync(RazorCodeActionContext context, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using var tasks = new PooledArrayBuilder<Task<ImmutableArray<RazorVSInternalCodeAction>>>();
foreach (var provider in _razorCodeActionProviders)
{
tasks.Add(provider.ProvideAsync(context, cancellationToken));
}
return await ConsolidateCodeActionsFromProvidersAsync(tasks.ToImmutable(), cancellationToken).ConfigureAwait(false);
}
private static async Task<ImmutableArray<RazorVSInternalCodeAction>> ConsolidateCodeActionsFromProvidersAsync(
ImmutableArray<Task<ImmutableArray<RazorVSInternalCodeAction>>> tasks,
CancellationToken cancellationToken)
{
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
using var codeActions = new PooledArrayBuilder<RazorVSInternalCodeAction>();
cancellationToken.ThrowIfCancellationRequested();
foreach (var result in results)
{
codeActions.AddRange(result);
}
return codeActions.ToImmutable();
}
private static ImmutableHashSet<string> GetAllAvailableCodeActionNames()
{
using var _ = ArrayBuilderPool<string>.GetPooledObject(out var availableCodeActionNames);
var refactoringProviderNames = typeof(RazorPredefinedCodeRefactoringProviderNames)
.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string))
.Select(property => property.GetValue(null) as string)
.WhereNotNull();
var codeFixProviderNames = typeof(RazorPredefinedCodeFixProviderNames)
.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string))
.Select(property => property.GetValue(null) as string)
.WhereNotNull();
availableCodeActionNames.AddRange(refactoringProviderNames);
availableCodeActionNames.AddRange(codeFixProviderNames);
availableCodeActionNames.Add(LanguageServerConstants.CodeActions.CodeActionFromVSCode);
return availableCodeActionNames.ToImmutableHashSet();
}
internal TestAccessor GetTestAccessor() => new(this);
internal readonly struct TestAccessor(CodeActionsService instance)
{
public Task<RazorCodeActionContext?> GenerateRazorCodeActionContextAsync(VSCodeActionParams request, IDocumentSnapshot documentSnapshot, bool supportsCodeActionResolve, CancellationToken cancellationToken)
=> instance.GenerateRazorCodeActionContextAsync(request, documentSnapshot, supportsCodeActionResolve, cancellationToken);
public Task<RazorVSInternalCodeAction[]> GetCodeActionsFromLanguageServerAsync(RazorLanguageKind languageKind, RazorCodeActionContext context, Guid correlationId, CancellationToken cancellationToken)
=> instance.GetCodeActionsFromLanguageServerAsync(languageKind, context, correlationId, cancellationToken);
}
}

Просмотреть файл

@ -5,17 +5,17 @@ using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class DefaultHtmlCodeActionProvider(IEditMappingService editMappingService) : IHtmlCodeActionProvider
internal sealed class HtmlCodeActionProvider(IEditMappingService editMappingService) : IHtmlCodeActionProvider
{
private readonly IEditMappingService _editMappingService = editMappingService;
@ -55,7 +55,7 @@ internal sealed class DefaultHtmlCodeActionProvider(IEditMappingService editMapp
foreach (var edit in documentEdits)
{
edit.Edits = HtmlFormatter.FixHtmlTextEdits(htmlSourceText, edit.Edits);
edit.Edits = FormattingUtilities.FixHtmlTextEdits(htmlSourceText, edit.Edits);
}
codeAction.Edit = new WorkspaceEdit

Просмотреть файл

@ -0,0 +1,38 @@
// 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.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class HtmlCodeActionResolver(
IDelegatedCodeActionResolver delegatedCodeActionResolver,
IEditMappingService editMappingService) : IHtmlCodeActionResolver
{
private readonly IDelegatedCodeActionResolver _delegatedCodeActionResolver = delegatedCodeActionResolver;
private readonly IEditMappingService _editMappingService = editMappingService;
public string Action => LanguageServerConstants.CodeActions.Default;
public async Task<CodeAction> ResolveAsync(
DocumentContext documentContext,
CodeAction codeAction,
CancellationToken cancellationToken)
{
var resolvedCodeAction = await _delegatedCodeActionResolver.ResolveCodeActionAsync(documentContext.GetTextDocumentIdentifier(), documentContext.Snapshot.Version, RazorLanguageKind.Html, codeAction, cancellationToken).ConfigureAwait(false);
if (resolvedCodeAction?.Edit?.DocumentChanges is null)
{
// Unable to resolve code action with server, return original code action
return codeAction;
}
await HtmlCodeActionProvider.RemapAndFixHtmlCodeActionEditAsync(_editMappingService, documentContext.Snapshot, resolvedCodeAction, cancellationToken).ConfigureAwait(false);
return resolvedCodeAction;
}
}

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface IHtmlCodeActionProvider : ICodeActionProvider
{

Просмотреть файл

@ -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.CodeAnalysis.Razor.CodeActions;
internal interface IHtmlCodeActionResolver : ICodeActionResolver
{
Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken);
}

Просмотреть файл

@ -1,13 +1,12 @@
// 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.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface ICodeActionProvider
{

Просмотреть файл

@ -0,0 +1,17 @@
// 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.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface ICodeActionResolveService
{
RazorCodeActionResolutionParams GetRazorCodeActionResolutionParams(CodeAction request);
Task<CodeAction> ResolveCodeActionAsync(DocumentContext documentContext, CodeAction request, RazorFormattingOptions options, CancellationToken cancellationToken);
}

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface ICodeActionResolver
{

Просмотреть файл

@ -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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface ICodeActionsService
{
Task<SumType<Command, CodeAction>[]?> GetCodeActionsAsync(VSCodeActionParams request, DocumentContext documentContext, bool supportsCodeActionResolve, Guid correlationId, CancellationToken cancellationToken);
}

Просмотреть файл

@ -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.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface IDelegatedCodeActionResolver
{
Task<CodeAction?> ResolveCodeActionAsync(TextDocumentIdentifier razorFileIdentifier, int hostDocumentVersion, RazorLanguageKind languageKind, CodeAction codeAction, CancellationToken cancellationToken);
}

Просмотреть файл

@ -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;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface IDelegatedCodeActionsProvider
{
Task<RazorVSInternalCodeAction[]> GetDelegatedCodeActionsAsync(RazorLanguageKind languageKind, VSCodeActionParams request, int hostDocumentVersion, Guid correlationId, CancellationToken cancellationToken);
}

Просмотреть файл

@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
internal sealed class AddUsingsCodeActionParams
{

Просмотреть файл

@ -1,14 +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;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
internal static class CodeActionExtensions
{

Просмотреть файл

@ -3,7 +3,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
internal sealed class CreateComponentCodeActionParams
{

Просмотреть файл

@ -3,7 +3,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
internal sealed class ExtractToCodeBehindCodeActionParams
{

Просмотреть файл

@ -3,7 +3,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
internal sealed class ExtractToComponentCodeActionParams
{

Просмотреть файл

@ -3,7 +3,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
internal sealed class GenerateMethodCodeActionParams
{

Просмотреть файл

@ -5,7 +5,7 @@ using System.Text.Json.Serialization;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
internal sealed class RazorCodeActionResolutionParams
{

Просмотреть файл

@ -5,7 +5,7 @@ using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Models;
[DataContract]
internal sealed class RazorVSInternalCodeAction : VSInternalCodeAction

Просмотреть файл

@ -7,19 +7,19 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class AddUsingsCodeActionResolver : IRazorCodeActionResolver
{
public string Action => LanguageServerConstants.CodeActions.AddUsing;
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken)
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken)
{
var actionParams = data.Deserialize<AddUsingsCodeActionParams>();
if (actionParams is null)

Просмотреть файл

@ -5,15 +5,12 @@ using System;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
internal static class CodeBlockService
{
@ -34,12 +31,12 @@ internal static class CodeBlockService
/// <see cref="FormattingUtilities.InitialIndent"/><see cref="FormattingUtilities.Indent"/>}
/// </param>
/// <param name="options">
/// The <see cref="RazorLSPOptions"/> that contains information about indentation.
/// The <see cref="RazorFormattingOptions"/> that contains information about indentation.
/// </param>
/// <returns>
/// A <see cref="TextEdit"/> that will place the formatted generated method within a @code block in the file.
/// </returns>
public static TextEdit[] CreateFormattedTextEdit(RazorCodeDocument code, string templateWithMethodSignature, RazorLSPOptions options)
public static TextEdit[] CreateFormattedTextEdit(RazorCodeDocument code, string templateWithMethodSignature, RazorFormattingOptions options)
{
var csharpCodeBlock = code.GetSyntaxTree().Root.DescendantNodes()
.Select(RazorSyntaxFacts.TryGetCSharpCodeFromCodeBlock)
@ -104,7 +101,7 @@ internal static class CodeBlockService
int openBraceLineIndex,
int closeBraceLineIndex,
SourceLocation insertLocation,
RazorLSPOptions options,
RazorFormattingOptions options,
string method)
{
// The absolute index and character index of the code block's location points to the end of '@code'.

Просмотреть файл

@ -8,27 +8,29 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
internal sealed class ComponentAccessibilityCodeActionProvider : IRazorCodeActionProvider
{
public async Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeActionContext context, CancellationToken cancellationToken)
{
// Locate cursor
var node = context.CodeDocument.GetSyntaxTree().Root.FindInnermostNode(context.StartLocation.AbsoluteIndex);
var node = context.CodeDocument.GetSyntaxTree().Root.FindInnermostNode(context.StartAbsoluteIndex);
if (node is null)
{
return [];
@ -45,7 +47,7 @@ internal sealed class ComponentAccessibilityCodeActionProvider : IRazorCodeActio
return [];
}
if (context.StartLocation.AbsoluteIndex < startTag.SpanStart)
if (context.StartAbsoluteIndex < startTag.SpanStart)
{
// Cursor is before the start tag, so we shouldn't show a light bulb. This can happen
// in cases where the cursor is in whitespace at the beginning of the document

Просмотреть файл

@ -10,13 +10,15 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class CreateComponentCodeActionResolver(LanguageServerFeatureOptions languageServerFeatureOptions) : IRazorCodeActionResolver
{
@ -24,7 +26,7 @@ internal sealed class CreateComponentCodeActionResolver(LanguageServerFeatureOpt
public string Action => LanguageServerConstants.CodeActions.CreateComponentFromTag;
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken)
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken)
{
var actionParams = data.Deserialize<CreateComponentCodeActionParams>();
if (actionParams is null)

Просмотреть файл

@ -1,7 +1,6 @@
// 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.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
@ -12,14 +11,14 @@ using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class ExtractToCodeBehindCodeActionProvider(ILoggerFactory loggerFactory) : IRazorCodeActionProvider
{
@ -43,7 +42,7 @@ internal sealed class ExtractToCodeBehindCodeActionProvider(ILoggerFactory logge
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
}
var owner = syntaxTree.Root.FindInnermostNode(context.StartLocation.AbsoluteIndex);
var owner = syntaxTree.Root.FindInnermostNode(context.StartAbsoluteIndex);
if (owner is null)
{
_logger.LogWarning($"Owner should never be null.");
@ -85,7 +84,7 @@ internal sealed class ExtractToCodeBehindCodeActionProvider(ILoggerFactory logge
}
// Do not provide code action if the cursor is inside the code block
if (context.StartLocation.AbsoluteIndex > csharpCodeBlockNode.SpanStart)
if (context.StartAbsoluteIndex > csharpCodeBlockNode.SpanStart)
{
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
}
@ -128,6 +127,6 @@ internal sealed class ExtractToCodeBehindCodeActionProvider(ILoggerFactory logge
// good namespace to extract to
=> codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out @namespace);
private static bool HasUnsupportedChildren(Language.Syntax.SyntaxNode node)
private static bool HasUnsupportedChildren(RazorSyntaxNode node)
=> node.DescendantNodes().Any(n => n is MarkupBlockSyntax or CSharpTransitionSyntax or RazorCommentBlockSyntax);
}

Просмотреть файл

@ -8,35 +8,32 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
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;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed class ExtractToCodeBehindCodeActionResolver(
LanguageServerFeatureOptions languageServerFeatureOptions,
IClientConnection clientConnection) : IRazorCodeActionResolver
IRoslynCodeActionHelpers roslynCodeActionHelpers) : IRazorCodeActionResolver
{
private static readonly Workspace s_workspace = new AdhocWorkspace();
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
private readonly IClientConnection _clientConnection = clientConnection;
private readonly IRoslynCodeActionHelpers _roslynCodeActionHelpers = roslynCodeActionHelpers;
public string Action => LanguageServerConstants.CodeActions.ExtractToCodeBehindAction;
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken)
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken)
{
var actionParams = data.Deserialize<ExtractToCodeBehindCodeActionParams>();
if (actionParams is null)
@ -137,19 +134,7 @@ internal sealed class ExtractToCodeBehindCodeActionResolver(
var newFileContent = builder.ToString();
var parameters = new FormatNewFileParams()
{
Project = new TextDocumentIdentifier
{
Uri = new Uri(project.FilePath, UriKind.Absolute)
},
Document = new TextDocumentIdentifier
{
Uri = codeBehindUri
},
Contents = newFileContent
};
var fixedContent = await _clientConnection.SendRequestAsync<FormatNewFileParams, string?>(CustomMessageNames.RazorFormatNewFileEndpointName, parameters, cancellationToken).ConfigureAwait(false);
var fixedContent = await _roslynCodeActionHelpers.GetFormattedNewFileContentsAsync(project.FilePath, codeBehindUri, newFileContent, cancellationToken).ConfigureAwait(false);
if (fixedContent is null)
{

Просмотреть файл

@ -1,27 +1,22 @@
// 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.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.CSharp.Syntax;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
internal sealed class ExtractToComponentCodeActionProvider() : IRazorCodeActionProvider
{
@ -77,7 +72,7 @@ internal sealed class ExtractToComponentCodeActionProvider() : IRazorCodeActionP
private static (SyntaxNode? Start, SyntaxNode? End) GetStartAndEndElements(RazorCodeActionContext context, RazorSyntaxTree syntaxTree)
{
var owner = syntaxTree.Root.FindInnermostNode(context.StartLocation.AbsoluteIndex, includeWhitespace: true);
var owner = syntaxTree.Root.FindInnermostNode(context.StartAbsoluteIndex, includeWhitespace: true);
if (owner is null)
{
return (null, null);
@ -85,34 +80,34 @@ internal sealed class ExtractToComponentCodeActionProvider() : IRazorCodeActionP
var startElementNode = owner.FirstAncestorOrSelf<SyntaxNode>(IsBlockNode);
if (startElementNode is null || LocationInvalid(context.StartLocation, startElementNode))
if (startElementNode is null || LocationInvalid(context.StartAbsoluteIndex, startElementNode))
{
return (null, null);
}
var endElementNode = context.StartLocation == context.EndLocation
var endElementNode = context.StartAbsoluteIndex == context.EndAbsoluteIndex
? startElementNode
: GetEndElementNode(context, syntaxTree);
return (startElementNode, endElementNode);
static bool LocationInvalid(SourceLocation location, SyntaxNode node)
static bool LocationInvalid(int location, SyntaxNode node)
{
// Make sure to test for cases where selection
// is inside of a markup tag such as <p>hello$ there</p>
if (node is MarkupElementSyntax markupElement)
{
return location.AbsoluteIndex > markupElement.StartTag.Span.End &&
location.AbsoluteIndex < markupElement.EndTag.SpanStart;
return location > markupElement.StartTag.Span.End &&
location < markupElement.EndTag.SpanStart;
}
return !node.Span.Contains(location.AbsoluteIndex);
return !node.Span.Contains(location);
}
}
private static SyntaxNode? GetEndElementNode(RazorCodeActionContext context, RazorSyntaxTree syntaxTree)
{
var endOwner = syntaxTree.Root.FindInnermostNode(context.EndLocation.AbsoluteIndex, includeWhitespace: true);
var endOwner = syntaxTree.Root.FindInnermostNode(context.EndAbsoluteIndex, includeWhitespace: true);
if (endOwner is null)
{
return null;

Просмотреть файл

@ -6,18 +6,19 @@ using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
internal sealed class ExtractToComponentCodeActionResolver(
LanguageServerFeatureOptions languageServerFeatureOptions) : IRazorCodeActionResolver
@ -26,7 +27,7 @@ internal sealed class ExtractToComponentCodeActionResolver(
public string Action => LanguageServerConstants.CodeActions.ExtractToNewComponentAction;
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken)
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken)
{
if (data.ValueKind == JsonValueKind.Undefined)
{

Просмотреть файл

@ -7,17 +7,18 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using SyntaxFacts = Microsoft.CodeAnalysis.CSharp.SyntaxFacts;
using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
internal sealed class GenerateMethodCodeActionProvider : IRazorCodeActionProvider
{
@ -30,7 +31,7 @@ internal sealed class GenerateMethodCodeActionProvider : IRazorCodeActionProvide
}
var syntaxTree = context.CodeDocument.GetSyntaxTree();
var owner = syntaxTree.Root.FindToken(context.StartLocation.AbsoluteIndex).Parent.AssumeNotNull();
var owner = syntaxTree.Root.FindToken(context.StartAbsoluteIndex).Parent.AssumeNotNull();
if (IsGenerateEventHandlerValid(owner, out var methodName, out var eventName))
{

Просмотреть файл

@ -11,12 +11,10 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
@ -24,16 +22,14 @@ using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using CSharpSyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
internal sealed class GenerateMethodCodeActionResolver(
RazorLSPOptionsMonitor razorLSPOptionsMonitor,
IClientConnection clientConnection,
IRoslynCodeActionHelpers roslynCodeActionHelpers,
IDocumentMappingService documentMappingService,
IRazorFormattingService razorFormattingService) : IRazorCodeActionResolver
{
private readonly RazorLSPOptionsMonitor _razorLSPOptionsMonitor = razorLSPOptionsMonitor;
private readonly IClientConnection _clientConnection = clientConnection;
private readonly IRoslynCodeActionHelpers _roslynCodeActionHelpers = roslynCodeActionHelpers;
private readonly IDocumentMappingService _documentMappingService = documentMappingService;
private readonly IRazorFormattingService _razorFormattingService = razorFormattingService;
@ -49,7 +45,7 @@ internal sealed class GenerateMethodCodeActionResolver(
public string Action => LanguageServerConstants.CodeActions.GenerateEventHandler;
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken)
public async Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken)
{
var actionParams = data.Deserialize<GenerateMethodCodeActionParams>();
if (actionParams is null)
@ -72,6 +68,7 @@ internal sealed class GenerateMethodCodeActionResolver(
documentContext,
razorNamespace: null,
razorClassName,
options,
cancellationToken).ConfigureAwait(false);
}
@ -85,6 +82,7 @@ internal sealed class GenerateMethodCodeActionResolver(
documentContext,
razorNamespace,
razorClassName,
options,
cancellationToken).ConfigureAwait(false);
}
@ -101,8 +99,8 @@ internal sealed class GenerateMethodCodeActionResolver(
var classLocationLineSpan = @class.GetLocation().GetLineSpan();
var formattedMethod = FormattingUtilities.AddIndentationToMethod(
templateWithMethodSignature,
_razorLSPOptionsMonitor.CurrentValue.TabSize,
_razorLSPOptionsMonitor.CurrentValue.InsertSpaces,
options.TabSize,
options.InsertSpaces,
@class.SpanStart,
classLocationLineSpan.StartLinePosition.Character,
content);
@ -112,15 +110,7 @@ internal sealed class GenerateMethodCodeActionResolver(
character: 0,
$"{formattedMethod}{Environment.NewLine}");
var delegatedParams = new DelegatedSimplifyMethodParams(
new TextDocumentIdentifierAndVersion(new TextDocumentIdentifier() { Uri = codeBehindUri }, 1),
RequiresVirtualDocument: false,
edit);
var result = await _clientConnection.SendRequestAsync<DelegatedSimplifyMethodParams, TextEdit[]?>(
CustomMessageNames.RazorSimplifyMethodEndpointName,
delegatedParams,
cancellationToken).ConfigureAwait(false);
var result = await _roslynCodeActionHelpers.GetSimplifiedTextEditsAsync(codeBehindUri, edit, requiresVirtualDocument: false, cancellationToken).ConfigureAwait(false);
var codeBehindTextDocEdit = new TextDocumentEdit()
{
@ -137,10 +127,11 @@ internal sealed class GenerateMethodCodeActionResolver(
DocumentContext documentContext,
string? razorNamespace,
string? razorClassName,
RazorFormattingOptions options,
CancellationToken cancellationToken)
{
var templateWithMethodSignature = await PopulateMethodSignatureAsync(documentContext, actionParams, cancellationToken).ConfigureAwait(false);
var edits = CodeBlockService.CreateFormattedTextEdit(code, templateWithMethodSignature, _razorLSPOptionsMonitor.CurrentValue);
var edits = CodeBlockService.CreateFormattedTextEdit(code, templateWithMethodSignature, options);
// If there are 3 edits, this means that there is no existing @code block, so we have an edit for '@code {', the method stub, and '}'.
// Otherwise, a singular edit means that an @code block does exist and the only edit is adding the method stub.
@ -161,11 +152,7 @@ internal sealed class GenerateMethodCodeActionResolver(
character: 0,
editToSendToRoslyn.NewText);
var delegatedParams = new DelegatedSimplifyMethodParams(documentContext.GetTextDocumentIdentifierAndVersion(), RequiresVirtualDocument: true, tempTextEdit);
var result = await _clientConnection.SendRequestAsync<DelegatedSimplifyMethodParams, TextEdit[]?>(
CustomMessageNames.RazorSimplifyMethodEndpointName,
delegatedParams,
cancellationToken).ConfigureAwait(false);
var result = await _roslynCodeActionHelpers.GetSimplifiedTextEditsAsync(documentContext.Uri, tempTextEdit, requiresVirtualDocument: true, cancellationToken).ConfigureAwait(false);
// Roslyn should have passed back 2 edits. One that contains the simplified method stub and the other that contains the new
// location for the class end brace since we had asked to insert the method stub at the original class end brace location.
@ -188,20 +175,15 @@ internal sealed class GenerateMethodCodeActionResolver(
.Replace(FormattingUtilities.Indent, string.Empty);
var remappedEdit = VsLspFactory.CreateTextEdit(remappedRange, unformattedMethodSignature);
var delegatedParams = new DelegatedSimplifyMethodParams(documentContext.GetTextDocumentIdentifierAndVersion(), RequiresVirtualDocument: true, remappedEdit);
var result = await _clientConnection.SendRequestAsync<DelegatedSimplifyMethodParams, TextEdit[]?>(
CustomMessageNames.RazorSimplifyMethodEndpointName,
delegatedParams,
cancellationToken).ConfigureAwait(false);
var result = await _roslynCodeActionHelpers.GetSimplifiedTextEditsAsync(documentContext.Uri, remappedEdit, requiresVirtualDocument: true, cancellationToken).ConfigureAwait(false);
if (result is not null)
{
var formattingOptions = new RazorFormattingOptions()
{
TabSize = _razorLSPOptionsMonitor.CurrentValue.TabSize,
InsertSpaces = _razorLSPOptionsMonitor.CurrentValue.InsertSpaces,
CodeBlockBraceOnNextLine = _razorLSPOptionsMonitor.CurrentValue.CodeBlockBraceOnNextLine
TabSize = options.TabSize,
InsertSpaces = options.InsertSpaces,
CodeBlockBraceOnNextLine = options.CodeBlockBraceOnNextLine
};
var formattedChange = await _razorFormattingService.TryGetCSharpCodeActionEditAsync(

Просмотреть файл

@ -1,13 +1,12 @@
// 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.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface IRazorCodeActionProvider
{

Просмотреть файл

@ -4,12 +4,13 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface IRazorCodeActionResolver : ICodeActionResolver
{
Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken);
Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken);
}

Просмотреть файл

@ -0,0 +1,15 @@
// 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.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal interface IRoslynCodeActionHelpers
{
Task<string?> GetFormattedNewFileContentsAsync(string projectFilePath, Uri csharpFileUri, string newFileContent, CancellationToken cancellationToken);
Task<TextEdit[]?> GetSimplifiedTextEditsAsync(Uri codeBehindUri, TextEdit edit, bool requiresVirtualDocument, CancellationToken cancellationToken);
}

Просмотреть файл

@ -3,11 +3,11 @@
using System;
using System.Text.Json;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
namespace Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
internal static class RazorCodeActionFactory
{

Просмотреть файл

@ -1,20 +1,19 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
namespace Microsoft.CodeAnalysis.Razor.CodeActions;
internal sealed record class RazorCodeActionContext(
VSCodeActionParams Request,
IDocumentSnapshot DocumentSnapshot,
RazorCodeDocument CodeDocument,
SourceLocation StartLocation,
SourceLocation EndLocation,
int StartAbsoluteIndex,
int EndAbsoluteIndex,
SourceText SourceText,
bool SupportsFileCreation,
bool SupportsCodeActionResolve);

Просмотреть файл

@ -2,11 +2,13 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.LanguageServer.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.Formatting;
@ -260,4 +262,21 @@ internal static class FormattingUtilities
return builder.DrainToImmutable();
}
}
/// <summary>
/// Sometimes the Html language server will send back an edit that contains a tilde, because the generated
/// document we send them has lots of tildes. In those cases, we need to do some extra work to compute the
/// minimal text edits
/// </summary>
public static TextEdit[] FixHtmlTextEdits(SourceText htmlSourceText, TextEdit[] edits)
{
// Avoid computing a minimal diff if we don't need to
if (!edits.Any(static e => e.NewText.Contains("~")))
return edits;
var changes = edits.SelectAsArray(htmlSourceText.GetTextChange);
var fixedChanges = htmlSourceText.MinimizeTextChanges(changes);
return [.. fixedChanges.Select(htmlSourceText.GetTextEdit)];
}
}

Просмотреть файл

@ -172,4 +172,19 @@
<data name="TagHelper_Element_Glyph" xml:space="preserve">
<value>Razor TagHelper Element Glyph</value>
</data>
<data name="Create_Component_FromTag_Title" xml:space="preserve">
<value>Create component from tag</value>
</data>
<data name="ExtractTo_CodeBehind_Title" xml:space="preserve">
<value>Extract block to code behind</value>
</data>
<data name="ExtractTo_Component_Title" xml:space="preserve">
<value>Extract element to new component</value>
</data>
<data name="Generate_Async_Event_Handler_Title" xml:space="preserve">
<value>Generate Async Event Handler '{0}'</value>
</data>
<data name="Generate_Event_Handler_Title" xml:space="preserve">
<value>Generate Event Handler '{0}'</value>
</data>
</root>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">Hodnota nesmí být null ani prázdný řetězec.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Diagnostika po:</target>
@ -39,6 +44,16 @@
<target state="translated">Úprava na {0} odstraní neprázdný obsah {1}.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Operace formátování je ukončována, protože by zavedla nebo odebrala jednu z více diagnostik.</target>
@ -49,6 +64,16 @@
<target state="translated">Operace formátování je ukončována, protože by přidala nebo odstranila neprázdný obsah.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Neplatný posun</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">Der Wert darf nicht NULL oder eine leere Zeichenfolge sein.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Diagnose nach:</target>
@ -39,6 +44,16 @@
<target state="translated">Das Bearbeiten bei {0} löscht den Nicht-Leerraum-Inhalt "{1}".</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Ein Formatierungsvorgang wird abgebrochen, weil er eine oder mehrere Diagnosen einführen oder entfernen würde.</target>
@ -49,6 +64,16 @@
<target state="translated">Ein Formatierungsvorgang wird abgebrochen, da er Nicht-Leerraum-Inhalte hinzufügen oder löschen würde.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Ungültiger Offset.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">El valor no puede ser nulo ni una cadena vacía.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Diagnósticos después de:</target>
@ -39,6 +44,16 @@
<target state="translated">Editar en {0} eliminar el contenido que no es un espacio en blanco "{1}" .</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Se está abandonando una operación de formato porque introduciría o quitaría uno de más diagnósticos.</target>
@ -49,6 +64,16 @@
<target state="translated">Se está abandonando una operación de formato porque agregaría o eliminaría contenido que no es un espacio en blanco.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Desplazamiento no válido.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">La valeur ne peut pas être Null ni être une chaîne vide.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Diagnostics après :</target>
@ -39,6 +44,16 @@
<target state="translated">Modifier à {0} supprime le contenu autre quun espace « {1} ».</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Une opération de formatage est en cours dabandon, car elle introduit ou supprime un ou plusieurs diagnostics.</target>
@ -49,6 +64,16 @@
<target state="translated">Une opération de formatage est en cours dabandon, car elle ajouterait ou supprimerait du contenu qui nest pas un espace blanc.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Décalage non valide.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">Il valore non può essere null o una stringa vuota.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Diagnostica dopo:</target>
@ -39,6 +44,16 @@
<target state="translated">La modifica in {0} elimina contenuti diversi da spazi vuoti '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">È in corso l'abbandono di un'operazione di formato perché comporterebbe l'introduzione o la rimozione di una o più operazioni di diagnostica.</target>
@ -49,6 +64,16 @@
<target state="translated">È in corso l'abbandono di un'operazione di formattazione perché aggiungerebbe o eliminerebbe contenuto diverso da spazi vuoti.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Offset non valido.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">値を null または空の文字列にすることはできません。</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">次の時間が経過した後の診断:</target>
@ -39,6 +44,16 @@
<target state="translated">{0} で削除すると空白以外のスペース '{1}' が追加されます。</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">書式設定操作は、さらに診断の 1 つを導入または削除するため、破棄されています。</target>
@ -49,6 +64,16 @@
<target state="translated">書式設定操作は、空白以外のコンテンツを追加または削除するため、破棄されています。</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">無効なオフセットです。</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">값은 null이거나 빈 문자열일 수 없습니다.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">다음 이후 진단:</target>
@ -39,6 +44,16 @@
<target state="translated">{0}에서 편집은 공백이 아닌 콘텐츠 '{1}'을(를) 삭제합니다.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">서식 작업이 중단되는 이유는 진단 중 하나를 더 도입하거나 제거할 수 있기 때문입니다.</target>
@ -49,6 +64,16 @@
<target state="translated">공백이 아닌 콘텐츠를 추가하거나 삭제하기 때문에 형식 작업이 중단됩니다.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">오프셋이 유효하지 않습니다.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">Wartość nie może być wartością null ani pustym ciągiem.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Diagnostyka po:</target>
@ -39,6 +44,16 @@
<target state="translated">Edycja na stronie {0} usuwa zawartość bez białych znaków „{1}”.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Operacja formatowania jest porzucana, ponieważ spowodowałaby wprowadzenie lub usunięcie jednej z większej liczby diagnostyki.</target>
@ -49,6 +64,16 @@
<target state="translated">Operacja formatowania jest porzucana, ponieważ doda lub usunie zawartość bez białych znaków.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Nieprawidłowe przesunięcie.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">O valor não pode ser nulo ou uma cadeia de caracteres vazia.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Diagnóstico após:</target>
@ -39,6 +44,16 @@
<target state="translated">Editar em {0} exclui o conteúdo que não é espaço em branco ''{1}''.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Uma operação de formato está sendo abandonada porque introduziria ou removeria um ou mais diagnósticos.</target>
@ -49,6 +64,16 @@
<target state="translated">Uma operação de formato está sendo abandonada porque adicionaria ou excluiria conteúdo que não é espaço em branco.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Deslocamento inválido.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">Значение не может быть NULL или пустой строкой.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Диагностика после:</target>
@ -39,6 +44,16 @@
<target state="translated">Редактирование {0} удаляет содержимое, не являющееся пробелами "{1}".</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Операция форматирования отменяется, поскольку она вводит или удаляет одну или несколько диагностик.</target>
@ -49,6 +64,16 @@
<target state="translated">Операция форматирования отменяется, поскольку она может добавить или удалить содержимое, отличное от пробелов.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Недопустимое смещение.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">Değer null veya boş bir dize olamaz.</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">Şundan sonraki tanılama:</target>
@ -39,6 +44,16 @@
<target state="translated">{0} konumunda düzenleme, '{1}' boşluk dışı içeriğini siler.</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">Bir veya daha fazla tanılamayı ortaya çıkarabileceği veya kaldırabileceği için biçimlendirme işlemi bırakılıyor.</target>
@ -49,6 +64,16 @@
<target state="translated">Boşluk dışı içeriği ekleyebileceği veya silebileceği için biçimlendirme işlemi bırakıl.</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">Geçersiz uzaklık.</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">值不能为 null 或空字符串。</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">诊断后:</target>
@ -39,6 +44,16 @@
<target state="translated">于 {0} 编辑,删除非空格内容“{1}”。</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">正在放弃格式操作,因为它会引入或删除其他诊断之一。</target>
@ -49,6 +64,16 @@
<target state="translated">正在放弃格式操作,因为它会添加或删除非空格内容。</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">偏移无效。</target>

Просмотреть файл

@ -7,6 +7,11 @@
<target state="translated">值不能為 Null 或空字串。</target>
<note />
</trans-unit>
<trans-unit id="Create_Component_FromTag_Title">
<source>Create component from tag</source>
<target state="new">Create component from tag</target>
<note />
</trans-unit>
<trans-unit id="Diagnostics_after">
<source>Diagnostics after:</source>
<target state="translated">在以下事項之後的診斷:</target>
@ -39,6 +44,16 @@
<target state="translated">在 {0} 處編輯將刪除非空白內容 '{1}'。</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_CodeBehind_Title">
<source>Extract block to code behind</source>
<target state="new">Extract block to code behind</target>
<note />
</trans-unit>
<trans-unit id="ExtractTo_Component_Title">
<source>Extract element to new component</source>
<target state="new">Extract element to new component</target>
<note />
</trans-unit>
<trans-unit id="Format_operation_changed_diagnostics">
<source>A format operation is being abandoned because it would introduce or remove one of more diagnostics.</source>
<target state="translated">將放棄格式化作業,因為它將引入或移除一個或多個診斷。</target>
@ -49,6 +64,16 @@
<target state="translated">將放棄格式化作業,因為它將新增或删除非空白內容。</target>
<note />
</trans-unit>
<trans-unit id="Generate_Async_Event_Handler_Title">
<source>Generate Async Event Handler '{0}'</source>
<target state="new">Generate Async Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Generate_Event_Handler_Title">
<source>Generate Event Handler '{0}'</source>
<target state="new">Generate Event Handler '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Invalid_Offset">
<source>Invalid offset.</source>
<target state="translated">位移無效。</target>

Просмотреть файл

@ -7,8 +7,10 @@ using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
using Xunit.Abstractions;
@ -76,7 +78,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
});
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.Null(workspaceEdit);
@ -99,7 +101,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -137,7 +139,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -182,7 +184,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -221,7 +223,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -258,7 +260,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -296,7 +298,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -330,7 +332,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -368,7 +370,7 @@ public class AddUsingsCodeActionResolverTest(ITestOutputHelper testOutput) : Lan
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);

Просмотреть файл

@ -7,11 +7,12 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Testing;
@ -22,20 +23,20 @@ using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
public class DefaultCSharpCodeActionProviderTest : LanguageServerTestBase
public class CSharpCodeActionProviderTest : LanguageServerTestBase
{
private readonly ImmutableArray<RazorVSInternalCodeAction> _supportedCodeActions;
private readonly ImmutableArray<RazorVSInternalCodeAction> _supportedImplicitExpressionCodeActions;
public DefaultCSharpCodeActionProviderTest(ITestOutputHelper testOutput)
public CSharpCodeActionProviderTest(ITestOutputHelper testOutput)
: base(testOutput)
{
_supportedCodeActions = DefaultCSharpCodeActionProvider
_supportedCodeActions = CSharpCodeActionProvider
.SupportedDefaultCodeActionNames
.Select(name => new RazorVSInternalCodeAction { Name = name })
.ToImmutableArray();
_supportedImplicitExpressionCodeActions = DefaultCSharpCodeActionProvider
_supportedImplicitExpressionCodeActions = CSharpCodeActionProvider
.SupportedImplicitExpressionCodeActionNames
.Select(name => new RazorVSInternalCodeAction { Name = name })
.ToImmutableArray();
@ -56,11 +57,10 @@ public class DefaultCSharpCodeActionProviderTest : LanguageServerTestBase
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(8, 4));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
var provider = new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
// Act
var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);
@ -87,11 +87,10 @@ public class DefaultCSharpCodeActionProviderTest : LanguageServerTestBase
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: false);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: false);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
var provider = new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
// Act
var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);
@ -115,11 +114,10 @@ public class DefaultCSharpCodeActionProviderTest : LanguageServerTestBase
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(13, 4));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(13, 4));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
var provider = new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
// Act
var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);
@ -148,11 +146,10 @@ $$Path;
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(13, 4));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(13, 4));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
var provider = new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
// Act
var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);
@ -182,11 +179,10 @@ $$Path;
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(13, 4));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(13, 4));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
var provider = new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
// Act
var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);
@ -213,11 +209,10 @@ $$Path;
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(8, 4));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
var provider = new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
ImmutableArray<RazorVSInternalCodeAction> codeActions =
[
@ -250,12 +245,11 @@ $$Path;
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(8, 4));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var options = new ConfigurableLanguageServerFeatureOptions(new[] { $"--{nameof(ConfigurableLanguageServerFeatureOptions.ShowAllCSharpCodeActions)}" });
var provider = new DefaultCSharpCodeActionProvider(options);
var provider = new CSharpCodeActionProvider(options);
ImmutableArray<RazorVSInternalCodeAction> codeActions =
[
@ -296,11 +290,10 @@ $$Path;
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(8, 4));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
var provider = new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance);
// Act
var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);
@ -314,7 +307,7 @@ $$Path;
private static RazorCodeActionContext CreateRazorCodeActionContext(
VSCodeActionParams request,
SourceLocation location,
int absoluteIndex,
string filePath,
string text,
SourceSpan componentSourceSpan,
@ -347,8 +340,8 @@ $$Path;
request,
documentSnapshotMock.Object,
codeDocument,
location,
location,
StartAbsoluteIndex: absoluteIndex,
EndAbsoluteIndex: absoluteIndex,
codeDocument.Source.Text,
supportsFileCreation,
supportsCodeActionResolve);

Просмотреть файл

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
@ -23,7 +24,7 @@ using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
public class DefaultCSharpCodeActionResolverTest(ITestOutputHelper testOutput) : LanguageServerTestBase(testOutput)
public class CSharpCodeActionResolverTest(ITestOutputHelper testOutput) : LanguageServerTestBase(testOutput)
{
private static readonly CodeAction s_defaultResolvedCodeAction = new()
{
@ -160,7 +161,7 @@ public class DefaultCSharpCodeActionResolverTest(ITestOutputHelper testOutput) :
}
private static void CreateCodeActionResolver(
out DefaultCSharpCodeActionResolver csharpCodeActionResolver,
out CSharpCodeActionResolver csharpCodeActionResolver,
out DocumentContext documentContext,
IClientConnection? clientConnection = null,
IRazorFormattingService? razorFormattingService = null)
@ -173,8 +174,9 @@ public class DefaultCSharpCodeActionResolverTest(ITestOutputHelper testOutput) :
clientConnection ??= CreateLanguageServer();
razorFormattingService ??= CreateRazorFormattingService(documentUri);
csharpCodeActionResolver = new DefaultCSharpCodeActionResolver(
clientConnection,
var delegatedCodeActionResolver = new DelegatedCodeActionResolver(clientConnection);
csharpCodeActionResolver = new CSharpCodeActionResolver(
delegatedCodeActionResolver,
razorFormattingService);
documentContext = CreateDocumentContext(documentUri, codeDocument);

Просмотреть файл

@ -9,9 +9,10 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
@ -41,8 +42,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
},
};
var location = new SourceLocation(0, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0));
var context = CreateRazorCodeActionContext(request, absoluteIndex: 0, documentPath, contents, new SourceSpan(0, 0));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new TypeAccessibilityCodeActionProvider();
@ -96,8 +96,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
}
};
var location = new SourceLocation(0, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0), supportsCodeActionResolve: false);
var context = CreateRazorCodeActionContext(request, absoluteIndex: 0, documentPath, contents, new SourceSpan(0, 0), supportsCodeActionResolve: false);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new TypeAccessibilityCodeActionProvider();
@ -138,8 +137,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
}
};
var location = new SourceLocation(0, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0));
var context = CreateRazorCodeActionContext(request, absoluteIndex: 0, documentPath, contents, new SourceSpan(0, 0));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new TypeAccessibilityCodeActionProvider();
@ -189,8 +187,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
}
};
var location = new SourceLocation(0, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: false);
var context = CreateRazorCodeActionContext(request, absoluteIndex: 0, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: false);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new TypeAccessibilityCodeActionProvider();
@ -248,8 +245,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
}
};
var location = new SourceLocation(8, 0, 8);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: true);
var context = CreateRazorCodeActionContext(request, absoluteIndex: 8, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: true);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new TypeAccessibilityCodeActionProvider();
@ -300,8 +296,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
}
};
var location = new SourceLocation(0, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: true);
var context = CreateRazorCodeActionContext(request, absoluteIndex: 0, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: true);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new TypeAccessibilityCodeActionProvider();
@ -375,8 +370,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
}
};
var location = new SourceLocation(0, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: false);
var context = CreateRazorCodeActionContext(request, absoluteIndex: 0, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: false);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new TypeAccessibilityCodeActionProvider();
@ -434,7 +428,7 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
private static RazorCodeActionContext CreateRazorCodeActionContext(
VSCodeActionParams request,
SourceLocation location,
int absoluteIndex,
string filePath,
string text,
SourceSpan componentSourceSpan,
@ -477,8 +471,8 @@ public class TypeAccessibilityCodeActionProviderTest(ITestOutputHelper testOutpu
request,
documentSnapshotMock.Object,
codeDocument,
location,
location,
StartAbsoluteIndex: absoluteIndex,
EndAbsoluteIndex: absoluteIndex,
codeDocument.Source.Text,
supportsFileCreation,
supportsCodeActionResolve);

Просмотреть файл

@ -10,16 +10,18 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
using Microsoft.AspNetCore.Razor.Utilities;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Razor.Workspaces;
@ -41,14 +43,12 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
private const string CodeBehindTestReplaceNamespace = "$$Replace_Namespace$$";
private GenerateMethodCodeActionResolver[] CreateRazorCodeActionResolvers(
IClientConnection clientConnection,
IRazorFormattingService razorFormattingService,
RazorLSPOptionsMonitor? optionsMonitor = null)
IRoslynCodeActionHelpers roslynCodeActionHelpers,
IRazorFormattingService razorFormattingService)
=>
[
new GenerateMethodCodeActionResolver(
optionsMonitor ?? TestRazorLSPOptionsMonitor.Create(),
clientConnection,
roslynCodeActionHelpers,
new LspDocumentMappingService(FilePathService, new TestDocumentContextFactory(), LoggerFactory),
razorFormattingService)
];
@ -1038,11 +1038,13 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
Assert.NotNull(codeActionToRun);
var formattingService = await TestRazorFormattingService.CreateWithFullSupportAsync(LoggerFactory);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(languageServer);
var changes = await GetEditsAsync(
codeActionToRun,
requestContext,
languageServer,
CreateRazorCodeActionResolvers(languageServer, formattingService));
optionsMonitor: null,
CreateRazorCodeActionResolvers(roslynCodeActionHelpers, formattingService));
var razorEdits = new List<TextChange>();
var codeBehindEdits = new List<TextChange>();
@ -1076,7 +1078,7 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
string codeAction,
int childActionIndex = 0,
IRazorCodeActionProvider[]? razorCodeActionProviders = null,
Func<IClientConnection, IRazorFormattingService, RazorLSPOptionsMonitor?, IRazorCodeActionResolver[]>? codeActionResolversCreator = null,
Func<IRoslynCodeActionHelpers, IRazorFormattingService, IRazorCodeActionResolver[]>? codeActionResolversCreator = null,
RazorLSPOptionsMonitor? optionsMonitor = null,
Diagnostic[]? diagnostics = null)
{
@ -1089,7 +1091,7 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
string codeAction,
int childActionIndex = 0,
IRazorCodeActionProvider[]? razorCodeActionProviders = null,
Func<IClientConnection, IRazorFormattingService, RazorLSPOptionsMonitor?, IRazorCodeActionResolver[]>? codeActionResolversCreator = null,
Func<IRoslynCodeActionHelpers, IRazorFormattingService, IRazorCodeActionResolver[]>? codeActionResolversCreator = null,
RazorLSPOptionsMonitor? optionsMonitor = null,
Diagnostic[]? diagnostics = null)
{
@ -1124,11 +1126,13 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
Assert.NotNull(codeActionToRun);
var formattingService = await TestRazorFormattingService.CreateWithFullSupportAsync(LoggerFactory, codeDocument, optionsMonitor?.CurrentValue);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(languageServer);
var changes = await GetEditsAsync(
codeActionToRun,
requestContext,
languageServer,
codeActionResolversCreator?.Invoke(languageServer, formattingService, optionsMonitor) ?? []);
optionsMonitor,
codeActionResolversCreator?.Invoke(roslynCodeActionHelpers, formattingService) ?? []);
var edits = new List<TextChange>();
foreach (var change in changes)
@ -1160,19 +1164,23 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
IRazorCodeActionProvider[]? razorProviders = null,
Diagnostic[]? diagnostics = null)
{
var endpoint = new CodeActionEndpoint(
var delegatedCodeActionsProvider = new DelegatedCodeActionsProvider(clientConnection, NoOpTelemetryReporter.Instance, LoggerFactory);
var codeActionsService = new CodeActionsService(
DocumentMappingService.AssumeNotNull(),
razorCodeActionProviders: razorProviders ?? [],
csharpCodeActionProviders:
[
new DefaultCSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance),
new CSharpCodeActionProvider(TestLanguageServerFeatureOptions.Instance),
new TypeAccessibilityCodeActionProvider()
],
htmlCodeActionProviders: [],
clientConnection,
LanguageServerFeatureOptions.AssumeNotNull(),
LoggerFactory,
telemetryReporter: null);
delegatedCodeActionsProvider,
LanguageServerFeatureOptions.AssumeNotNull());
var endpoint = new CodeActionEndpoint(
codeActionsService,
NoOpTelemetryReporter.Instance);
// Call GetRegistration, so the endpoint knows we support resolve
endpoint.ApplyCapabilities(new(), new VSInternalClientCapabilities
@ -1202,18 +1210,22 @@ public class CodeActionEndToEndTest(ITestOutputHelper testOutput) : SingleServer
VSInternalCodeAction codeActionToRun,
RazorRequestContext requestContext,
IClientConnection clientConnection,
RazorLSPOptionsMonitor? optionsMonitor,
IRazorCodeActionResolver[] razorResolvers)
{
var formattingService = await TestRazorFormattingService.CreateWithFullSupportAsync(LoggerFactory);
var csharpResolvers = new CSharpCodeActionResolver[]
var delegatedCodeActionResolver = new DelegatedCodeActionResolver(clientConnection);
var csharpResolvers = new ICSharpCodeActionResolver[]
{
new DefaultCSharpCodeActionResolver(clientConnection, formattingService)
new CSharpCodeActionResolver(delegatedCodeActionResolver, formattingService)
};
var htmlResolvers = Array.Empty<HtmlCodeActionResolver>();
var htmlResolvers = Array.Empty<IHtmlCodeActionResolver>();
var resolveEndpoint = new CodeActionResolveEndpoint(razorResolvers, csharpResolvers, htmlResolvers, LoggerFactory);
optionsMonitor ??= TestRazorLSPOptionsMonitor.Create();
var codeActionResolveService = new CodeActionResolveService(razorResolvers, csharpResolvers, htmlResolvers, LoggerFactory);
var resolveEndpoint = new CodeActionResolveEndpoint(codeActionResolveService, optionsMonitor);
var resolveResult = await resolveEndpoint.HandleRequestAsync(codeActionToRun, requestContext, DisposalToken);

Просмотреть файл

@ -10,10 +10,12 @@ using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
@ -456,7 +458,7 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
var documentPath = new Uri("C:/path/to/Page.razor");
var codeDocument = CreateCodeDocument("@code {}");
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var codeActionEndpoint = CreateEndpoint(razorCodeActionProviders: [CreateRazorCodeActionProvider()]);
var codeActionService = CreateService(razorCodeActionProviders: [CreateRazorCodeActionProvider()]);
var initialRange = VsLspFactory.CreateZeroWidthRange(0, 1);
var selectionRange = VsLspFactory.CreateZeroWidthRange(0, 5);
@ -471,7 +473,7 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
};
// Act
var razorCodeActionContext = await codeActionEndpoint.GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, DisposalToken);
var razorCodeActionContext = await codeActionService.GetTestAccessor().GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, supportsCodeActionResolve: false, DisposalToken);
// Assert
Assert.NotNull(razorCodeActionContext);
@ -485,7 +487,7 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
var documentPath = new Uri("C:/path/to/Page.razor");
var codeDocument = CreateCodeDocument("@code {}");
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var codeActionEndpoint = CreateEndpoint(razorCodeActionProviders: [CreateRazorCodeActionProvider()]);
var codeActionService = CreateService(razorCodeActionProviders: [CreateRazorCodeActionProvider()]);
var initialRange = VsLspFactory.CreateZeroWidthRange(0, 1);
var request = new VSCodeActionParams()
@ -499,7 +501,7 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
};
// Act
var razorCodeActionContext = await codeActionEndpoint.GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, DisposalToken);
var razorCodeActionContext = await codeActionService.GetTestAccessor().GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, supportsCodeActionResolve: false, DisposalToken);
// Assert
Assert.NotNull(razorCodeActionContext);
@ -514,7 +516,7 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
var codeDocument = CreateCodeDocument("@code {}");
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var codeActionEndpoint = CreateEndpoint(csharpCodeActionProviders: [CreateCSharpCodeActionProvider()]);
var codeActionService = CreateService(csharpCodeActionProviders: [CreateCSharpCodeActionProvider()]);
var initialRange = VsLspFactory.CreateZeroWidthRange(0, 1);
var request = new VSCodeActionParams()
@ -524,11 +526,11 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
Context = new VSInternalCodeActionContext()
};
var context = await codeActionEndpoint.GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, DisposalToken);
var context = await codeActionService.GetTestAccessor().GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, supportsCodeActionResolve: false, DisposalToken);
Assert.NotNull(context);
// Act
var results = await codeActionEndpoint.GetCodeActionsFromLanguageServerAsync(RazorLanguageKind.CSharp, documentContext, context, Guid.Empty, cancellationToken: default);
var results = await codeActionService.GetTestAccessor().GetCodeActionsFromLanguageServerAsync(RazorLanguageKind.CSharp, context, Guid.Empty, cancellationToken: default);
// Assert
Assert.Empty(results);
@ -544,7 +546,7 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var projectedRange = VsLspFactory.CreateZeroWidthRange(15, 2);
var codeActionEndpoint = CreateEndpoint(
var codeActionService = CreateService(
documentMappingService: CreateDocumentMappingService(projectedRange.ToLinePositionSpan()),
csharpCodeActionProviders: [CreateCSharpCodeActionProvider()],
clientConnection: TestClientConnection.Instance);
@ -560,11 +562,11 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
}
};
var context = await codeActionEndpoint.GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, DisposalToken);
var context = await codeActionService.GetTestAccessor().GenerateRazorCodeActionContextAsync(request, documentContext.Snapshot, supportsCodeActionResolve: false, DisposalToken);
Assert.NotNull(context);
// Act
var results = await codeActionEndpoint.GetCodeActionsFromLanguageServerAsync(RazorLanguageKind.CSharp, documentContext, context, Guid.Empty, cancellationToken: DisposalToken);
var results = await codeActionService.GetTestAccessor().GetCodeActionsFromLanguageServerAsync(RazorLanguageKind.CSharp, context, Guid.Empty, cancellationToken: DisposalToken);
// Assert
var result = Assert.Single(results);
@ -591,18 +593,42 @@ public class CodeActionEndpointTest(ITestOutputHelper testOutput) : LanguageServ
LanguageServerFeatureOptions? languageServerFeatureOptions = null,
bool supportsCodeActionResolve = false)
{
var codeActionsService = CreateService(
documentMappingService,
razorCodeActionProviders,
csharpCodeActionProviders,
htmlCodeActionProviders,
clientConnection,
languageServerFeatureOptions);
return new CodeActionEndpoint(
codeActionsService,
NoOpTelemetryReporter.Instance)
{
_supportsCodeActionResolve = supportsCodeActionResolve
};
}
private CodeActionsService CreateService(
IDocumentMappingService? documentMappingService = null,
ImmutableArray<IRazorCodeActionProvider> razorCodeActionProviders = default,
ImmutableArray<ICSharpCodeActionProvider> csharpCodeActionProviders = default,
ImmutableArray<IHtmlCodeActionProvider> htmlCodeActionProviders = default,
IClientConnection? clientConnection = null,
LanguageServerFeatureOptions? languageServerFeatureOptions = null)
{
var delegatedCodeActionsProvider = new DelegatedCodeActionsProvider(
clientConnection ?? StrictMock.Of<IClientConnection>(),
NoOpTelemetryReporter.Instance,
LoggerFactory);
return new CodeActionsService(
documentMappingService ?? CreateDocumentMappingService(),
razorCodeActionProviders.NullToEmpty(),
csharpCodeActionProviders.NullToEmpty(),
htmlCodeActionProviders.NullToEmpty(),
clientConnection ?? StrictMock.Of<IClientConnection>(),
languageServerFeatureOptions ?? StrictMock.Of<LanguageServerFeatureOptions>(x => x.SupportsFileManipulation == true),
LoggerFactory,
telemetryReporter: null)
{
_supportsCodeActionResolve = supportsCodeActionResolve
};
delegatedCodeActionsProvider,
languageServerFeatureOptions ?? StrictMock.Of<LanguageServerFeatureOptions>(x => x.SupportsFileManipulation == true));
}
private static IDocumentMappingService CreateDocumentMappingService(LinePositionSpan? projectedRange = null)

Просмотреть файл

@ -5,14 +5,14 @@ using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Moq;
using Xunit;
using Xunit.Abstractions;
@ -25,13 +25,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
new IRazorCodeActionResolver[] {
new MockRazorCodeActionResolver("Test"),
},
Array.Empty<CSharpCodeActionResolver>(),
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [new MockRazorCodeActionResolver("Test")],
csharpCodeActionResolvers: [],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -61,13 +62,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
Array.Empty<IRazorCodeActionResolver>(),
new CSharpCodeActionResolver[] {
new MockCSharpCodeActionResolver("Test"),
},
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [],
[new MockCSharpCodeActionResolver("Test")],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -93,15 +95,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
new IRazorCodeActionResolver[] {
new MockRazorCodeActionResolver("TestRazor"),
},
new CSharpCodeActionResolver[] {
new MockCSharpCodeActionResolver("TestCSharp"),
},
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [new MockRazorCodeActionResolver("TestRazor")],
csharpCodeActionResolvers: [new MockCSharpCodeActionResolver("TestCSharp")],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -127,11 +128,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
Array.Empty<IRazorCodeActionResolver>(),
Array.Empty<CSharpCodeActionResolver>(),
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [],
csharpCodeActionResolvers: [],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -166,11 +170,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
Array.Empty<IRazorCodeActionResolver>(),
Array.Empty<CSharpCodeActionResolver>(),
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [],
csharpCodeActionResolvers: [],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -201,13 +208,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
Array.Empty<IRazorCodeActionResolver>(),
new CSharpCodeActionResolver[] {
new MockCSharpCodeActionResolver("Test"),
},
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [],
csharpCodeActionResolvers: [new MockCSharpCodeActionResolver("Test")],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -242,13 +250,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
new IRazorCodeActionResolver[] {
new MockRazorCodeActionResolver("Test"),
},
Array.Empty<CSharpCodeActionResolver>(),
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [new MockRazorCodeActionResolver("Test")],
csharpCodeActionResolvers: [],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -279,13 +288,13 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
new IRazorCodeActionResolver[] {
new MockRazorCodeActionResolver("A"),
new MockRazorNullCodeActionResolver("B"),
},
Array.Empty<CSharpCodeActionResolver>(),
Array.Empty<HtmlCodeActionResolver>(),
var service = new CodeActionResolveService(
razorCodeActionResolvers: [
new MockRazorCodeActionResolver("A"),
new MockRazorNullCodeActionResolver("B"),
],
csharpCodeActionResolvers: [],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeAction = new CodeAction();
var request = new RazorCodeActionResolutionParams()
@ -300,7 +309,7 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
};
// Act
var resolvedCodeAction = await codeActionEndpoint.GetTestAccessor().ResolveRazorCodeActionAsync(documentContext, codeAction, request, DisposalToken);
var resolvedCodeAction = await service.GetTestAccessor().ResolveRazorCodeActionAsync(documentContext, codeAction, request, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(resolvedCodeAction.Edit);
@ -311,13 +320,13 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
new IRazorCodeActionResolver[] {
var service = new CodeActionResolveService(
razorCodeActionResolvers: [
new MockRazorNullCodeActionResolver("A"),
new MockRazorCodeActionResolver("B"),
},
Array.Empty<CSharpCodeActionResolver>(),
Array.Empty<HtmlCodeActionResolver>(),
],
csharpCodeActionResolvers: [],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeAction = new CodeAction();
var request = new RazorCodeActionResolutionParams()
@ -332,7 +341,7 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
};
// Act
var resolvedCodeAction = await codeActionEndpoint.GetTestAccessor().ResolveRazorCodeActionAsync(documentContext, codeAction, request, DisposalToken);
var resolvedCodeAction = await service.GetTestAccessor().ResolveRazorCodeActionAsync(documentContext, codeAction, request, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(resolvedCodeAction.Edit);
@ -343,13 +352,13 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
Array.Empty<IRazorCodeActionResolver>(),
new CSharpCodeActionResolver[] {
var service = new CodeActionResolveService(
razorCodeActionResolvers: [],
csharpCodeActionResolvers: [
new MockCSharpCodeActionResolver("A"),
new MockCSharpNullCodeActionResolver("B"),
},
Array.Empty<HtmlCodeActionResolver>(),
],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeAction = new CodeAction();
var request = new RazorCodeActionResolutionParams()
@ -360,7 +369,7 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
};
// Act
var resolvedCodeAction = await codeActionEndpoint.GetTestAccessor().ResolveCSharpCodeActionAsync(documentContext, codeAction, request, DisposalToken);
var resolvedCodeAction = await service.GetTestAccessor().ResolveCSharpCodeActionAsync(documentContext, codeAction, request, DisposalToken);
// Assert
Assert.NotNull(resolvedCodeAction.Edit);
@ -371,13 +380,13 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
Array.Empty<IRazorCodeActionResolver>(),
new CSharpCodeActionResolver[] {
var service = new CodeActionResolveService(
razorCodeActionResolvers: [],
csharpCodeActionResolvers: [
new MockCSharpNullCodeActionResolver("A"),
new MockCSharpCodeActionResolver("B"),
},
Array.Empty<HtmlCodeActionResolver>(),
],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeAction = new CodeAction();
var request = new RazorCodeActionResolutionParams()
@ -388,7 +397,7 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
};
// Act
var resolvedCodeAction = await codeActionEndpoint.GetTestAccessor().ResolveCSharpCodeActionAsync(documentContext, codeAction, request, DisposalToken);
var resolvedCodeAction = await service.GetTestAccessor().ResolveCSharpCodeActionAsync(documentContext, codeAction, request, DisposalToken);
// Assert
Assert.NotNull(resolvedCodeAction.Edit);
@ -399,16 +408,16 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
new IRazorCodeActionResolver[] {
var service = new CodeActionResolveService(
razorCodeActionResolvers: [
new MockRazorNullCodeActionResolver("A"),
new MockRazorCodeActionResolver("B"),
},
new CSharpCodeActionResolver[] {
],
csharpCodeActionResolvers: [
new MockCSharpNullCodeActionResolver("C"),
new MockCSharpCodeActionResolver("D"),
},
Array.Empty<HtmlCodeActionResolver>(),
],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeAction = new CodeAction();
var request = new RazorCodeActionResolutionParams()
@ -419,7 +428,7 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
};
// Act
var resolvedCodeAction = await codeActionEndpoint.GetTestAccessor().ResolveCSharpCodeActionAsync(documentContext, codeAction, request, DisposalToken);
var resolvedCodeAction = await service.GetTestAccessor().ResolveCSharpCodeActionAsync(documentContext, codeAction, request, DisposalToken);
// Assert
Assert.NotNull(resolvedCodeAction.Edit);
@ -430,13 +439,14 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
{
// Arrange
var documentContext = TestDocumentContext.Create(new Uri("C:/path/to/Page.razor"));
var codeActionEndpoint = new CodeActionResolveEndpoint(
Array.Empty<IRazorCodeActionResolver>(),
new CSharpCodeActionResolver[] {
new MockCSharpCodeActionResolver("Test"),
},
Array.Empty<HtmlCodeActionResolver>(),
var codeActionResolveService = new CodeActionResolveService(
razorCodeActionResolvers: [],
csharpCodeActionResolvers: [new MockCSharpCodeActionResolver("Test")],
htmlCodeActionResolvers: [],
LoggerFactory);
var codeActionEndpoint = new CodeActionResolveEndpoint(
codeActionResolveService,
TestRazorLSPOptionsMonitor.Create());
var requestParams = new RazorCodeActionResolutionParams()
{
TextDocument = (VSTextDocumentIdentifier)documentContext.GetTextDocumentIdentifier(),
@ -468,7 +478,7 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
Action = action;
}
public Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken)
public Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken)
{
return Task.FromResult<WorkspaceEdit?>(new WorkspaceEdit());
}
@ -476,47 +486,45 @@ public class CodeActionResolutionEndpointTest(ITestOutputHelper testOutput) : La
private class MockRazorNullCodeActionResolver : IRazorCodeActionResolver
{
public string Action { get; }
public string Action { get; }
internal MockRazorNullCodeActionResolver(string action)
{
Action = action;
}
public Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, CancellationToken cancellationToken)
public Task<WorkspaceEdit?> ResolveAsync(DocumentContext documentContext, JsonElement data, RazorFormattingOptions options, CancellationToken cancellationToken)
{
return SpecializedTasks.Null<WorkspaceEdit>();
}
}
private class MockCSharpCodeActionResolver : CSharpCodeActionResolver
private class MockCSharpCodeActionResolver : ICSharpCodeActionResolver
{
public override string Action { get; }
public string Action { get; }
internal MockCSharpCodeActionResolver(string action)
: base(Mock.Of<IClientConnection>(MockBehavior.Strict))
{
Action = action;
}
public override Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken)
public Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken)
{
codeAction.Edit = new WorkspaceEdit();
return Task.FromResult(codeAction);
}
}
private class MockCSharpNullCodeActionResolver : CSharpCodeActionResolver
private class MockCSharpNullCodeActionResolver : ICSharpCodeActionResolver
{
public override string Action { get; }
public string Action { get; }
internal MockCSharpNullCodeActionResolver(string action)
: base(Mock.Of<IClientConnection>(MockBehavior.Strict))
{
Action = action;
}
public override Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken)
public Task<CodeAction> ResolveAsync(DocumentContext documentContext, CodeAction codeAction, CancellationToken cancellationToken)
{
// This is deliberately returning null when it's not supposed to, so that if this code action
// is ever returned by a method, the test will fail

Просмотреть файл

@ -7,9 +7,10 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
@ -22,7 +23,7 @@ using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
public class DefaultHtmlCodeActionProviderTest(ITestOutputHelper testOutput) : LanguageServerTestBase(testOutput)
public class HtmlCodeActionProviderTest(ITestOutputHelper testOutput) : LanguageServerTestBase(testOutput)
{
[Fact]
public async Task ProvideAsync_WrapsResolvableCodeActions()
@ -39,12 +40,11 @@ public class DefaultHtmlCodeActionProviderTest(ITestOutputHelper testOutput) : L
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var documentMappingService = StrictMock.Of<IEditMappingService>();
var provider = new DefaultHtmlCodeActionProvider(documentMappingService);
var provider = new HtmlCodeActionProvider(documentMappingService);
ImmutableArray<RazorVSInternalCodeAction> codeActions = [new RazorVSInternalCodeAction() { Name = "Test" }];
@ -72,8 +72,7 @@ public class DefaultHtmlCodeActionProviderTest(ITestOutputHelper testOutput) : L
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var remappedEdit = new WorkspaceEdit
@ -95,7 +94,7 @@ public class DefaultHtmlCodeActionProviderTest(ITestOutputHelper testOutput) : L
.Setup(x => x.RemapWorkspaceEditAsync(It.IsAny<IDocumentSnapshot>(), It.IsAny<WorkspaceEdit>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(remappedEdit);
var provider = new DefaultHtmlCodeActionProvider(editMappingServiceMock.Object);
var provider = new HtmlCodeActionProvider(editMappingServiceMock.Object);
ImmutableArray<RazorVSInternalCodeAction> codeActions =
[
@ -140,7 +139,7 @@ public class DefaultHtmlCodeActionProviderTest(ITestOutputHelper testOutput) : L
private static RazorCodeActionContext CreateRazorCodeActionContext(
VSCodeActionParams request,
SourceLocation location,
int absoluteIndex,
string filePath,
string text,
bool supportsFileCreation = true,
@ -166,8 +165,8 @@ public class DefaultHtmlCodeActionProviderTest(ITestOutputHelper testOutput) : L
request,
documentSnapshotMock.Object,
codeDocument,
location,
location,
StartAbsoluteIndex: absoluteIndex,
EndAbsoluteIndex: absoluteIndex,
codeDocument.Source.Text,
supportsFileCreation,
supportsCodeActionResolve);

Просмотреть файл

@ -4,10 +4,11 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
@ -20,7 +21,7 @@ using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
public class DefaultHtmlCodeActionResolverTest(ITestOutputHelper testOutput) : LanguageServerTestBase(testOutput)
public class HtmlCodeActionResolverTest(ITestOutputHelper testOutput) : LanguageServerTestBase(testOutput)
{
[Fact]
public async Task ResolveAsync_RemapsAndFixesEdits()
@ -58,7 +59,8 @@ public class DefaultHtmlCodeActionResolverTest(ITestOutputHelper testOutput) : L
.Setup(x => x.RemapWorkspaceEditAsync(It.IsAny<IDocumentSnapshot>(), It.IsAny<WorkspaceEdit>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(remappedEdit);
var resolver = new DefaultHtmlCodeActionResolver(CreateLanguageServer(resolvedCodeAction), editMappingServiceMock.Object);
var delegatedCodeActionResolver = new DelegatedCodeActionResolver(CreateLanguageServer(resolvedCodeAction));
var resolver = new HtmlCodeActionResolver(delegatedCodeActionResolver, editMappingServiceMock.Object);
var codeAction = new RazorVSInternalCodeAction()
{

Просмотреть файл

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Testing;
@ -16,7 +17,7 @@ using Microsoft.VisualStudio.LanguageServer.Protocol;
using Moq;
using Xunit;
using Xunit.Abstractions;
using LanguageServerSR = Microsoft.AspNetCore.Razor.LanguageServer.Resources.SR;
using WorkspacesSR = Microsoft.CodeAnalysis.Razor.Workspaces.Resources.SR;
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
@ -39,8 +40,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 1));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(0, 1));
var provider = new ComponentAccessibilityCodeActionProvider();
@ -68,8 +68,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(0, 0));
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -98,8 +97,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9));
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9));
var provider = new ComponentAccessibilityCodeActionProvider();
@ -127,8 +125,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: true);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -152,7 +149,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
},
e =>
{
Assert.Equal(LanguageServerSR.Create_Component_FromTag_Title, e.Title);
Assert.Equal(WorkspacesSR.Create_Component_FromTag_Title, e.Title);
Assert.NotNull(e.Data);
Assert.Null(e.Edit);
});
@ -175,8 +172,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("CompOnent", StringComparison.Ordinal), 9), supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("CompOnent", StringComparison.Ordinal), 9), supportsFileCreation: true);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -200,7 +196,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
},
e =>
{
Assert.Equal(LanguageServerSR.Create_Component_FromTag_Title, e.Title);
Assert.Equal(WorkspacesSR.Create_Component_FromTag_Title, e.Title);
Assert.NotNull(e.Data);
Assert.Null(e.Edit);
});
@ -225,8 +221,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("CompOnent", StringComparison.Ordinal), 9), supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("CompOnent", StringComparison.Ordinal), 9), supportsFileCreation: true);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -244,7 +239,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
},
e =>
{
Assert.Equal(LanguageServerSR.Create_Component_FromTag_Title, e.Title);
Assert.Equal(WorkspacesSR.Create_Component_FromTag_Title, e.Title);
Assert.NotNull(e.Data);
Assert.Null(e.Edit);
});
@ -267,8 +262,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("GenericComponent", StringComparison.Ordinal), "GenericComponent".Length), supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("GenericComponent", StringComparison.Ordinal), "GenericComponent".Length), supportsFileCreation: true);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -292,7 +286,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
},
e =>
{
Assert.Equal(LanguageServerSR.Create_Component_FromTag_Title, e.Title);
Assert.Equal(WorkspacesSR.Create_Component_FromTag_Title, e.Title);
Assert.NotNull(e.Data);
Assert.Null(e.Edit);
});
@ -315,8 +309,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: true);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -325,7 +318,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
// Assert
var command = Assert.Single(commandOrCodeActionContainer);
Assert.Equal(LanguageServerSR.Create_Component_FromTag_Title, command.Title);
Assert.Equal(WorkspacesSR.Create_Component_FromTag_Title, command.Title);
Assert.NotNull(command.Data);
}
@ -346,8 +339,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: true);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -356,7 +348,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
// Assert
var command = Assert.Single(commandOrCodeActionContainer);
Assert.Equal(LanguageServerSR.Create_Component_FromTag_Title, command.Title);
Assert.Equal(WorkspacesSR.Create_Component_FromTag_Title, command.Title);
Assert.NotNull(command.Data);
}
@ -377,8 +369,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: false);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: false);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -406,8 +397,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: false);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: false);
var provider = new ComponentAccessibilityCodeActionProvider();
@ -431,7 +421,7 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
});
}
private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionParams request, SourceLocation location, string filePath, string text, SourceSpan componentSourceSpan, bool supportsFileCreation = true)
private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionParams request, int absoluteIndex, string filePath, string text, SourceSpan componentSourceSpan, bool supportsFileCreation = true)
{
var shortComponent = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Fully.Qualified.Component", "TestAssembly");
shortComponent.CaseSensitive = true;
@ -478,8 +468,8 @@ public class ComponentAccessibilityCodeActionProviderTest(ITestOutputHelper test
request,
documentSnapshotMock.Object,
codeDocument,
location,
location,
StartAbsoluteIndex: absoluteIndex,
EndAbsoluteIndex: absoluteIndex,
codeDocument.Source.Text,
supportsFileCreation,
SupportsCodeActionResolve: true);

Просмотреть файл

@ -6,10 +6,12 @@ using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
using Xunit.Abstractions;
@ -35,7 +37,7 @@ public class CreateComponentCodeActionResolverTest(ITestOutputHelper testOutput)
});
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.Null(workspaceEdit);
@ -58,7 +60,7 @@ public class CreateComponentCodeActionResolverTest(ITestOutputHelper testOutput)
});
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.Null(workspaceEdit);
@ -81,7 +83,7 @@ public class CreateComponentCodeActionResolverTest(ITestOutputHelper testOutput)
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -112,7 +114,7 @@ public class CreateComponentCodeActionResolverTest(ITestOutputHelper testOutput)
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);

Просмотреть файл

@ -10,9 +10,10 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Testing;
@ -44,8 +45,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
context.CodeDocument.SetFileKind(FileKinds.Legacy);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -75,8 +75,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -105,8 +104,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -135,8 +133,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -170,8 +167,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -211,8 +207,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, supportsFileCreation: true);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -254,8 +249,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, supportsFileCreation: true);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, supportsFileCreation: true);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -291,8 +285,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, supportsFileCreation: false);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, supportsFileCreation: false);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -327,8 +320,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = new VSInternalCodeActionContext()
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -364,8 +356,7 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Context = null!
};
var location = new SourceLocation(cursorPosition, -1, -1);
var context = CreateRazorCodeActionContext(request, location, documentPath, contents, relativePath: null);
var context = CreateRazorCodeActionContext(request, cursorPosition, documentPath, contents, relativePath: null);
var provider = new ExtractToCodeBehindCodeActionProvider(LoggerFactory);
@ -376,10 +367,10 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
Assert.Empty(commandOrCodeActionContainer);
}
private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionParams request, SourceLocation location, string filePath, string text, bool supportsFileCreation = true)
=> CreateRazorCodeActionContext(request, location, filePath, text, relativePath: filePath, supportsFileCreation: supportsFileCreation);
private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionParams request, int absoluteIndex, string filePath, string text, bool supportsFileCreation = true)
=> CreateRazorCodeActionContext(request, absoluteIndex, filePath, text, relativePath: filePath, supportsFileCreation: supportsFileCreation);
private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionParams request, SourceLocation location, string filePath, string text, string? relativePath, bool supportsFileCreation = true)
private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionParams request, int absoluteIndex, string filePath, string text, string? relativePath, bool supportsFileCreation = true)
{
var sourceDocument = RazorSourceDocument.Create(text, RazorSourceDocumentProperties.Create(filePath, relativePath));
var options = RazorParserOptions.Create(o =>
@ -409,8 +400,8 @@ public class ExtractToCodeBehindCodeActionProviderTest(ITestOutputHelper testOut
request,
documentSnapshotMock.Object,
codeDocument,
location,
location,
StartAbsoluteIndex: absoluteIndex,
EndAbsoluteIndex: absoluteIndex,
codeDocument.Source.Text,
supportsFileCreation,
SupportsCodeActionResolve: true);

Просмотреть файл

@ -7,11 +7,13 @@ using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Test;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -33,7 +35,7 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
{
// Arrange
var documentPath = new Uri("c:\\Test.razor");
var contents = $$"""
var contents = """
@page "/test"
@code { private int x = 1; }
""";
@ -41,11 +43,12 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
codeDocument.SetUnsupported();
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var data = JsonSerializer.SerializeToElement(CreateExtractToCodeBehindCodeActionParams(contents, "@code", "Test"));
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.Null(workspaceEdit);
@ -56,7 +59,7 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
{
// Arrange
var documentPath = new Uri("c:\\Test.razor");
var contents = $$"""
var contents = """
@page "/test"
@code { private int x = 1; }
""";
@ -64,11 +67,12 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
codeDocument.SetFileKind(FileKinds.Legacy);
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var data = JsonSerializer.SerializeToElement(CreateExtractToCodeBehindCodeActionParams(contents, "@code", "Test"));
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.Null(workspaceEdit);
@ -90,12 +94,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -154,12 +159,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -226,12 +232,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -308,12 +315,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -392,12 +400,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -464,12 +473,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@functions", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -528,12 +538,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -594,12 +605,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, _languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(_languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);
@ -664,12 +676,13 @@ public class ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOut
});
var documentContext = CreateDocumentContext(documentPath, codeDocument);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, languageServer);
var roslynCodeActionHelpers = new RoslynCodeActionHelpers(languageServer);
var resolver = new ExtractToCodeBehindCodeActionResolver(TestLanguageServerFeatureOptions.Instance, roslynCodeActionHelpers);
var actionParams = CreateExtractToCodeBehindCodeActionParams(contents, "@code", @namespace);
var data = JsonSerializer.SerializeToElement(actionParams);
// Act
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, DisposalToken);
var workspaceEdit = await resolver.ResolveAsync(documentContext, data, new RazorFormattingOptions(), DisposalToken);
// Assert
Assert.NotNull(workspaceEdit);

Просмотреть файл

@ -10,11 +10,11 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Testing;
@ -439,8 +439,8 @@ public class ExtractToComponentCodeActionProviderTest(ITestOutputHelper testOutp
request,
documentSnapshot.Object,
codeDocument,
new SourceLocation(selectionSpan.Start, -1, -1),
new SourceLocation(selectionSpan.End, -1, -1),
selectionSpan.Start,
selectionSpan.End,
sourceText,
supportsFileCreation,
SupportsCodeActionResolve: true);

Просмотреть файл

@ -8,7 +8,6 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
using Microsoft.AspNetCore.Razor.ProjectSystem;
@ -16,6 +15,7 @@ using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.ProjectSystem;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -303,6 +303,7 @@ public class ExtractToComponentCodeActionResolverTest(ITestOutputHelper testOutp
codeActionToRun,
requestContext,
languageServer,
optionsMonitor: null,
[resolver]
);

Просмотреть файл

@ -6,9 +6,9 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.CodeActions;
using Microsoft.CodeAnalysis.Razor.Protocol.Diagnostics;

Просмотреть файл

@ -3,13 +3,11 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -302,7 +300,7 @@ public class WrapWithTagEndpointTest(ITestOutputHelper testOutput) : LanguageSer
};
var htmlSourceText = await context.GetHtmlSourceTextAsync(DisposalToken);
var edits = HtmlFormatter.FixHtmlTextEdits(htmlSourceText, computedEdits);
var edits = FormattingUtilities.FixHtmlTextEdits(htmlSourceText, computedEdits);
Assert.Same(computedEdits, edits);
var finalText = inputSourceText.WithChanges(edits.Select(inputSourceText.GetTextChange));
@ -342,7 +340,7 @@ public class WrapWithTagEndpointTest(ITestOutputHelper testOutput) : LanguageSer
};
var htmlSourceText = await context.GetHtmlSourceTextAsync(DisposalToken);
var edits = HtmlFormatter.FixHtmlTextEdits(htmlSourceText, computedEdits);
var edits = FormattingUtilities.FixHtmlTextEdits(htmlSourceText, computedEdits);
Assert.NotSame(computedEdits, edits);
var finalText = inputSourceText.WithChanges(edits.Select(inputSourceText.GetTextChange));
@ -382,7 +380,7 @@ public class WrapWithTagEndpointTest(ITestOutputHelper testOutput) : LanguageSer
};
var htmlSourceText = await context.GetHtmlSourceTextAsync(DisposalToken);
var edits = HtmlFormatter.FixHtmlTextEdits(htmlSourceText, computedEdits);
var edits = FormattingUtilities.FixHtmlTextEdits(htmlSourceText, computedEdits);
Assert.NotSame(computedEdits, edits);
var finalText = inputSourceText.WithChanges(edits.Select(inputSourceText.GetTextChange));