зеркало из https://github.com/dotnet/razor.git
Call Roslyn to format new code behind documents (#9263)
Fixes https://github.com/dotnet/razor/issues/4330
Fixes https://github.com/dotnet/razor/issues/8766
Includes https://github.com/dotnet/razor/pull/9262 so just review from
8a2b8affa2
onwards
Goes with https://github.com/dotnet/roslyn/pull/69878 and
https://github.com/dotnet/vscode-csharp/pull/6329
I logged https://github.com/dotnet/razor/issues/9264 to follow up with a
better test, though strictly speaking the one I've added id exhaustive
:)
Behaviour of Extract to Code Behind before this change. Note the many
using statements and block scoped namespace.
![ExtractToCodeBehindBefore](https://github.com/dotnet/razor/assets/754264/10ac5595-b3b2-44c2-a1a7-66664d3c8f1b)
Behaviour after this change. Note the file scoped namespace, and file
header.
![ExtractToCodeBehindAfter](https://github.com/dotnet/razor/assets/754264/65160715-bc98-4336-b19d-37f9b14adf94)
The squiggle on `NavigationManager` is due to an `.editorconfig` rule I
have on in that project, requiring `this.` qualification, so is
unrelated.
This commit is contained in:
Коммит
da56c4410b
|
@ -9,6 +9,7 @@ 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.CodeActions.Razor;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.PooledObjects;
|
||||
|
@ -29,13 +30,16 @@ internal sealed class ExtractToCodeBehindCodeActionResolver : IRazorCodeActionRe
|
|||
|
||||
private readonly DocumentContextFactory _documentContextFactory;
|
||||
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
|
||||
private readonly ClientNotifierServiceBase _languageServer;
|
||||
|
||||
public ExtractToCodeBehindCodeActionResolver(
|
||||
DocumentContextFactory documentContextFactory,
|
||||
LanguageServerFeatureOptions languageServerFeatureOptions)
|
||||
LanguageServerFeatureOptions languageServerFeatureOptions,
|
||||
ClientNotifierServiceBase languageServer)
|
||||
{
|
||||
_documentContextFactory = documentContextFactory ?? throw new ArgumentNullException(nameof(documentContextFactory));
|
||||
_languageServerFeatureOptions = languageServerFeatureOptions;
|
||||
_languageServerFeatureOptions = languageServerFeatureOptions ?? throw new ArgumentNullException(nameof(languageServerFeatureOptions));
|
||||
_languageServer = languageServer ?? throw new ArgumentNullException(nameof(languageServer));
|
||||
}
|
||||
|
||||
public string Action => LanguageServerConstants.CodeActions.ExtractToCodeBehindAction;
|
||||
|
@ -93,8 +97,8 @@ internal sealed class ExtractToCodeBehindCodeActionResolver : IRazorCodeActionRe
|
|||
}
|
||||
|
||||
var className = Path.GetFileNameWithoutExtension(path);
|
||||
var codeBlockContent = text.GetSubTextString(new CodeAnalysis.Text.TextSpan(actionParams.ExtractStart, actionParams.ExtractEnd - actionParams.ExtractStart));
|
||||
var codeBehindContent = GenerateCodeBehindClass(className, actionParams.Namespace, codeBlockContent, codeDocument);
|
||||
var codeBlockContent = text.GetSubTextString(new CodeAnalysis.Text.TextSpan(actionParams.ExtractStart, actionParams.ExtractEnd - actionParams.ExtractStart)).Trim();
|
||||
var codeBehindContent = await GenerateCodeBehindClassAsync(documentContext.Project, codeBehindUri, className, actionParams.Namespace, codeBlockContent, codeDocument, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var start = codeDocument.Source.Lines.GetLocation(actionParams.RemoveStart);
|
||||
var end = codeDocument.Source.Lines.GetLocation(actionParams.RemoveEnd);
|
||||
|
@ -169,17 +173,7 @@ internal sealed class ExtractToCodeBehindCodeActionResolver : IRazorCodeActionRe
|
|||
return codeBehindPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a complete C# compilation unit containing a partial class
|
||||
/// with the given name, body contents, and the namespace and all
|
||||
/// usings from the existing code document.
|
||||
/// </summary>
|
||||
/// <param name="className">Name of the resultant partial class.</param>
|
||||
/// <param name="namespaceName">Name of the namespace to put the resultant class in.</param>
|
||||
/// <param name="contents">Class body contents.</param>
|
||||
/// <param name="razorCodeDocument">Existing code document we're extracting from.</param>
|
||||
/// <returns></returns>
|
||||
private string GenerateCodeBehindClass(string className, string namespaceName, string contents, RazorCodeDocument razorCodeDocument)
|
||||
private async Task<string> GenerateCodeBehindClassAsync(CodeAnalysis.Razor.ProjectSystem.IProjectSnapshot project, Uri codeBehindUri, string className, string namespaceName, string contents, RazorCodeDocument razorCodeDocument, CancellationToken cancellationToken)
|
||||
{
|
||||
using var _ = StringBuilderPool.GetPooledObject(out var builder);
|
||||
|
||||
|
@ -210,12 +204,32 @@ internal sealed class ExtractToCodeBehindCodeActionResolver : IRazorCodeActionRe
|
|||
builder.AppendLine(contents);
|
||||
builder.Append('}');
|
||||
|
||||
// Sadly we can't use a "real" workspace here, because we don't have access. If we use our workspace, it wouldn't have the right settings
|
||||
// for C# formatting, only Razor formatting, and we have no access to Roslyn's real workspace, since it could be in another process.
|
||||
// TODO: Rather than format here, call Roslyn via LSP to format, and remove and sort usings: https://github.com/dotnet/razor/issues/8766
|
||||
var node = CSharpSyntaxTree.ParseText(builder.ToString()).GetRoot();
|
||||
node = Formatter.Format(node, s_workspace);
|
||||
var newFileContent = builder.ToString();
|
||||
|
||||
return node.ToFullString();
|
||||
var parameters = new FormatNewFileParams()
|
||||
{
|
||||
Project = new TextDocumentIdentifier
|
||||
{
|
||||
Uri = new Uri(project.FilePath, UriKind.Absolute)
|
||||
},
|
||||
Document = new TextDocumentIdentifier
|
||||
{
|
||||
Uri = codeBehindUri
|
||||
},
|
||||
Contents = newFileContent
|
||||
};
|
||||
var fixedContent = await _languageServer.SendRequestAsync<FormatNewFileParams, string?>(CustomMessageNames.RazorFormatNewFileEndpointName, parameters, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (fixedContent is null)
|
||||
{
|
||||
// Sadly we can't use a "real" workspace here, because we don't have access. If we use our workspace, it wouldn't have the right settings
|
||||
// for C# formatting, only Razor formatting, and we have no access to Roslyn's real workspace, since it could be in another process.
|
||||
var node = await CSharpSyntaxTree.ParseText(newFileContent).GetRootAsync(cancellationToken).ConfigureAwait(false);
|
||||
node = Formatter.Format(node, s_workspace);
|
||||
|
||||
return node.ToFullString();
|
||||
}
|
||||
|
||||
return fixedContent;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
|
||||
|
||||
[DataContract]
|
||||
internal record FormatNewFileParams
|
||||
{
|
||||
[DataMember(Name = "document")]
|
||||
public required TextDocumentIdentifier Document { get; set; }
|
||||
|
||||
[DataMember(Name = "project")]
|
||||
public required TextDocumentIdentifier Project { get; set; }
|
||||
|
||||
[DataMember(Name = "contents")]
|
||||
public required string Contents { get; set; }
|
||||
}
|
|
@ -34,6 +34,8 @@ internal static class CustomMessageNames
|
|||
public const string RazorFoldingRangeEndpoint = "razor/foldingRange";
|
||||
public const string RazorHtmlFormattingEndpoint = "razor/htmlFormatting";
|
||||
public const string RazorHtmlOnTypeFormattingEndpoint = "razor/htmlOnTypeFormatting";
|
||||
public const string RazorSimplifyMethodEndpointName = "razor/simplifyMethod";
|
||||
public const string RazorFormatNewFileEndpointName = "razor/formatNewFile";
|
||||
|
||||
// VS Windows only at the moment, but could/should be migrated
|
||||
public const string RazorDocumentSymbolEndpoint = "razor/documentSymbol";
|
||||
|
@ -52,8 +54,6 @@ internal static class CustomMessageNames
|
|||
|
||||
public const string RazorReferencesEndpointName = "razor/references";
|
||||
|
||||
public const string RazorSimplifyMethodEndpointName = "razor/simplifyMethod";
|
||||
|
||||
// Called to get C# diagnostics from Roslyn when publishing diagnostics for VS Code
|
||||
public const string RazorCSharpPullDiagnosticsEndpointName = "razor/csharpPullDiagnostics";
|
||||
}
|
||||
|
|
|
@ -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.AspNetCore.Razor.LanguageServer.CodeActions.Razor;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using StreamJsonRpc;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServerClient.Razor;
|
||||
|
||||
internal partial class RazorCustomMessageTarget
|
||||
{
|
||||
[JsonRpcMethod(CustomMessageNames.RazorFormatNewFileEndpointName, UseSingleObjectParameterDeserialization = true)]
|
||||
public async Task<string?> FormatNewFileAsync(FormatNewFileParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
// This endpoint is special because it deals with a file that doesn't exist yet, so there is no document syncing necessary!
|
||||
var response = await _requestInvoker.ReinvokeRequestOnServerAsync<FormatNewFileParams, string?>(
|
||||
RazorLSPConstants.RoslynFormatNewFileEndpointName,
|
||||
RazorLSPConstants.RazorCSharpLanguageServerName,
|
||||
SupportsFormatNewFile,
|
||||
request,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return response.Result;
|
||||
}
|
||||
|
||||
private static bool SupportsFormatNewFile(JToken token)
|
||||
{
|
||||
var serverCapabilities = token.ToObject<VSInternalServerCapabilities>();
|
||||
|
||||
return serverCapabilities?.Experimental is JObject experimental
|
||||
&& experimental.TryGetValue(RazorLSPConstants.RoslynFormatNewFileEndpointName, out var supportsFormatNewFile)
|
||||
&& supportsFormatNewFile.ToObject<bool>();
|
||||
}
|
||||
}
|
|
@ -22,4 +22,6 @@ internal static class RazorLSPConstants
|
|||
public const string HtmlLSPDelegationContentTypeName = "html-delegation";
|
||||
|
||||
public const string RoslynSimplifyMethodEndpointName = "roslyn/simplifyMethod";
|
||||
|
||||
public const string RoslynFormatNewFileEndpointName = "roslyn/formatNewFile";
|
||||
}
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
// 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.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Test;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.Test.Common;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
@ -21,18 +24,23 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
|
|||
public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
||||
{
|
||||
private readonly DocumentContextFactory _emptyDocumentContextFactory;
|
||||
private readonly TestLanguageServer _languageServer;
|
||||
|
||||
public ExtractToCodeBehindCodeActionResolverTest(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
_emptyDocumentContextFactory = new TestDocumentContextFactory();
|
||||
_languageServer = new TestLanguageServer(new Dictionary<string, Func<object?, Task<object>>>()
|
||||
{
|
||||
[CustomMessageNames.RazorFormatNewFileEndpointName] = c => Task.FromResult<object>(null!),
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_MissingFile()
|
||||
{
|
||||
// Arrange
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(_emptyDocumentContextFactory, TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(_emptyDocumentContextFactory, TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var data = JObject.FromObject(new ExtractToCodeBehindCodeActionParams()
|
||||
{
|
||||
Uri = new Uri("c:/Test.razor"),
|
||||
|
@ -59,7 +67,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
codeDocument.SetUnsupported();
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var data = JObject.FromObject(CreateExtractToCodeBehindCodeActionParams(new Uri("c:/Test.razor"), contents, "@code", "Test"));
|
||||
|
||||
// Act
|
||||
|
@ -78,7 +86,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
codeDocument.SetFileKind(FileKinds.Legacy);
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var data = JObject.FromObject(CreateExtractToCodeBehindCodeActionParams(new Uri("c:/Test.razor"), contents, "@code", "Test"));
|
||||
|
||||
// Act
|
||||
|
@ -103,7 +111,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -165,7 +173,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -235,7 +243,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -315,7 +323,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -397,7 +405,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -467,7 +475,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@functions", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -529,7 +537,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -593,7 +601,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, _languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
|
@ -641,6 +649,60 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
|
|||
editCodeBehindEdit.NewText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_ExtractCodeBlock_CallsRoslyn()
|
||||
{
|
||||
// Arrange
|
||||
var documentPath = new Uri("c:/Test.razor");
|
||||
var contents = """
|
||||
@page "/test"
|
||||
|
||||
@code {
|
||||
private int x = 1;
|
||||
}
|
||||
""";
|
||||
var codeDocument = CreateCodeDocument(contents);
|
||||
Assert.True(codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace));
|
||||
|
||||
var languageServer = new TestLanguageServer(new Dictionary<string, Func<object?, Task<object>>>()
|
||||
{
|
||||
[CustomMessageNames.RazorFormatNewFileEndpointName] = c => Task.FromResult<object>("Hi there! I'm from Roslyn"),
|
||||
});
|
||||
|
||||
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance, languageServer);
|
||||
var actionParams = CreateExtractToCodeBehindCodeActionParams(documentPath, contents, "@code", @namespace);
|
||||
var data = JObject.FromObject(actionParams);
|
||||
|
||||
// Act
|
||||
var workspaceEdit = await resolver.ResolveAsync(data, default);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(workspaceEdit);
|
||||
Assert.NotNull(workspaceEdit!.DocumentChanges);
|
||||
Assert.Equal(3, workspaceEdit.DocumentChanges!.Value.Count());
|
||||
|
||||
var documentChanges = workspaceEdit.DocumentChanges!.Value.ToArray();
|
||||
var createFileChange = documentChanges[0];
|
||||
Assert.True(createFileChange.TryGetSecond(out var _));
|
||||
|
||||
var editCodeDocumentChange = documentChanges[1];
|
||||
Assert.True(editCodeDocumentChange.TryGetFirst(out var textDocumentEdit1));
|
||||
var editCodeDocumentEdit = textDocumentEdit1!.Edits.First();
|
||||
Assert.True(editCodeDocumentEdit.Range.Start.TryGetAbsoluteIndex(codeDocument.GetSourceText(), Logger, out var removeStart));
|
||||
Assert.Equal(actionParams.RemoveStart, removeStart);
|
||||
Assert.True(editCodeDocumentEdit.Range.End.TryGetAbsoluteIndex(codeDocument.GetSourceText(), Logger, out var removeEnd));
|
||||
Assert.Equal(actionParams.RemoveEnd, removeEnd);
|
||||
|
||||
var editCodeBehindChange = documentChanges[2];
|
||||
Assert.True(editCodeBehindChange.TryGetFirst(out var textDocumentEdit2));
|
||||
var editCodeBehindEdit = textDocumentEdit2!.Edits.First();
|
||||
|
||||
AssertEx.EqualOrDiff("""
|
||||
Hi there! I'm from Roslyn
|
||||
""",
|
||||
editCodeBehindEdit.NewText);
|
||||
}
|
||||
|
||||
private static RazorCodeDocument CreateCodeDocument(string text)
|
||||
{
|
||||
var projectItem = new TestRazorProjectItem("c:/Test.razor", "c:/Test.razor", "Test.razor") { Content = text };
|
||||
|
|
Загрузка…
Ссылка в новой задаче