[VS Code] Fix code action and rename file paths (#8129)

This commit is contained in:
Allison Chou 2023-01-12 18:32:29 -08:00 коммит произвёл GitHub
Родитель 2f4f299d9e
Коммит 9880902719
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 75 добавлений и 24 удалений

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

@ -1,6 +1,7 @@
// 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.InteropServices;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.CodeAnalysis.Razor.Workspaces;
@ -21,4 +22,9 @@ internal class DefaultLanguageServerFeatureOptions : LanguageServerFeatureOption
public override bool SingleServerSupport => false;
public override bool SupportsDelegatedCodeActions => false;
// Code action and rename paths in Windows VS Code need to be prefixed with '/':
// https://github.com/dotnet/razor/issues/8131
public override bool ReturnCodeActionAndRenamePathsWithPrefixedSlash
=> RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
}

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

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.Common.Extensions;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;
@ -20,10 +21,12 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
internal class CreateComponentCodeActionResolver : RazorCodeActionResolver
{
private readonly DocumentContextFactory _documentContextFactory;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
public CreateComponentCodeActionResolver(DocumentContextFactory documentContextFactory)
public CreateComponentCodeActionResolver(DocumentContextFactory documentContextFactory, LanguageServerFeatureOptions languageServerFeatureOptions)
{
_documentContextFactory = documentContextFactory ?? throw new ArgumentNullException(nameof(documentContextFactory));
_languageServerFeatureOptions = languageServerFeatureOptions ?? throw new ArgumentException(nameof(languageServerFeatureOptions));
}
public override string Action => LanguageServerConstants.CodeActions.CreateComponentFromTag;
@ -58,10 +61,14 @@ internal class CreateComponentCodeActionResolver : RazorCodeActionResolver
return null;
}
// VS Code in Windows expects path to start with '/'
var updatedPath = _languageServerFeatureOptions.ReturnCodeActionAndRenamePathsWithPrefixedSlash && !actionParams.Path.StartsWith("/")
? '/' + actionParams.Path
: actionParams.Path;
var newComponentUri = new UriBuilder()
{
Scheme = Uri.UriSchemeFile,
Path = actionParams.Path,
Path = updatedPath,
Host = string.Empty,
}.Uri;

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

@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;
using CSharpSyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@ -28,10 +29,14 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
internal class ExtractToCodeBehindCodeActionResolver : RazorCodeActionResolver
{
private readonly DocumentContextFactory _documentContextFactory;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
public ExtractToCodeBehindCodeActionResolver(DocumentContextFactory documentContextFactory)
public ExtractToCodeBehindCodeActionResolver(
DocumentContextFactory documentContextFactory,
LanguageServerFeatureOptions languageServerFeatureOptions)
{
_documentContextFactory = documentContextFactory ?? throw new ArgumentNullException(nameof(documentContextFactory));
_languageServerFeatureOptions = languageServerFeatureOptions;
}
public override string Action => LanguageServerConstants.CodeActions.ExtractToCodeBehindAction;
@ -69,10 +74,16 @@ internal class ExtractToCodeBehindCodeActionResolver : RazorCodeActionResolver
}
var codeBehindPath = GenerateCodeBehindPath(path);
// VS Code in Windows expects path to start with '/'
var updatedCodeBehindPath = _languageServerFeatureOptions.ReturnCodeActionAndRenamePathsWithPrefixedSlash && !codeBehindPath.StartsWith("/")
? '/' + codeBehindPath
: codeBehindPath;
var codeBehindUri = new UriBuilder
{
Scheme = Uri.UriSchemeFile,
Path = codeBehindPath,
Path = updatedCodeBehindPath,
Host = string.Empty,
}.Uri;

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

@ -188,17 +188,27 @@ internal class RenameEndpoint : AbstractRazorDelegatingEndpoint<RenameParamsBrid
}
}
public static void AddFileRenameForComponent(List<SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>> documentChanges, DocumentSnapshot documentSnapshot, string newPath)
public void AddFileRenameForComponent(List<SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>> documentChanges, DocumentSnapshot documentSnapshot, string newPath)
{
// VS Code in Windows expects path to start with '/'
var updatedOldPath = _languageServerFeatureOptions.ReturnCodeActionAndRenamePathsWithPrefixedSlash && !documentSnapshot.FilePath.StartsWith("/")
? '/' + documentSnapshot.FilePath
: documentSnapshot.FilePath;
var oldUri = new UriBuilder
{
Path = documentSnapshot.FilePath,
Path = updatedOldPath,
Host = string.Empty,
Scheme = Uri.UriSchemeFile,
}.Uri;
// VS Code in Windows expects path to start with '/'
var updatedNewPath = _languageServerFeatureOptions.ReturnCodeActionAndRenamePathsWithPrefixedSlash && !newPath.StartsWith("/")
? '/' + newPath
: newPath;
var newUri = new UriBuilder
{
Path = newPath,
Path = updatedNewPath,
Host = string.Empty,
Scheme = Uri.UriSchemeFile,
}.Uri;
@ -219,7 +229,7 @@ internal class RenameEndpoint : AbstractRazorDelegatingEndpoint<RenameParamsBrid
return newPath;
}
private static async Task AddEditsForCodeDocumentAsync(
private async Task AddEditsForCodeDocumentAsync(
List<SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>> documentChanges,
IReadOnlyList<TagHelperDescriptor> originTagHelpers,
string newName,
@ -241,9 +251,13 @@ internal class RenameEndpoint : AbstractRazorDelegatingEndpoint<RenameParamsBrid
return;
}
// VS Code in Windows expects path to start with '/'
var updatedPath = _languageServerFeatureOptions.ReturnCodeActionAndRenamePathsWithPrefixedSlash && !documentSnapshot.FilePath.StartsWith("/")
? "/" + documentSnapshot.FilePath
: documentSnapshot.FilePath;
var uri = new UriBuilder
{
Path = documentSnapshot.FilePath,
Path = updatedPath,
Host = string.Empty,
Scheme = Uri.UriSchemeFile,
}.Uri;

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

@ -21,6 +21,10 @@ internal abstract class LanguageServerFeatureOptions
public abstract bool SupportsDelegatedCodeActions { get; }
// Code action and rename paths in Windows VS Code need to be prefixed with '/':
// https://github.com/dotnet/razor/issues/8131
public abstract bool ReturnCodeActionAndRenamePathsWithPrefixedSlash { get; }
public string GetRazorCSharpFilePath(string razorFilePath) => razorFilePath + CSharpVirtualDocumentSuffix;
public string GetRazorHtmlFilePath(string razorFilePath) => razorFilePath + HtmlVirtualDocumentSuffix;

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

@ -60,5 +60,7 @@ internal class VisualStudioWindowsLanguageServerFeatureOptions : LanguageServerF
public override bool SupportsDelegatedCodeActions => true;
public override bool ReturnCodeActionAndRenamePathsWithPrefixedSlash => false;
private bool IsCodespacesOrLiveshare => _lspEditorFeatureDetector.IsRemoteClient() || _lspEditorFeatureDetector.IsLiveShareHost();
}

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

@ -39,6 +39,8 @@ internal class VisualStudioMacLanguageServerFeatureOptions : LanguageServerFeatu
public override bool SupportsDelegatedCodeActions => true;
public override bool ReturnCodeActionAndRenamePathsWithPrefixedSlash => false;
private bool IsCodespacesOrLiveshare => _lspEditorFeatureDetector.IsRemoteClient() || _lspEditorFeatureDetector.IsLiveShareHost();
}

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

@ -36,7 +36,7 @@ public class CreateComponentCodeActionResolverTest : LanguageServerTestBase
public async Task Handle_MissingFile()
{
// Arrange
var resolver = new CreateComponentCodeActionResolver(_emptyDocumentContextFactory);
var resolver = new CreateComponentCodeActionResolver(_emptyDocumentContextFactory, TestLanguageServerFeatureOptions.Instance);
var data = JObject.FromObject(new CreateComponentCodeActionParams()
{
Uri = new Uri("c:/Test.razor"),
@ -59,7 +59,7 @@ public class CreateComponentCodeActionResolverTest : LanguageServerTestBase
var codeDocument = CreateCodeDocument(contents);
codeDocument.SetUnsupported();
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument));
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var data = JObject.FromObject(new CreateComponentCodeActionParams()
{
Uri = documentPath,
@ -82,7 +82,7 @@ public class CreateComponentCodeActionResolverTest : LanguageServerTestBase
var codeDocument = CreateCodeDocument(contents);
codeDocument.SetFileKind(FileKinds.Legacy);
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument));
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var data = JObject.FromObject(new CreateComponentCodeActionParams()
{
Uri = documentPath,
@ -104,7 +104,7 @@ public class CreateComponentCodeActionResolverTest : LanguageServerTestBase
var contents = $"@page \"/test\"";
var codeDocument = CreateCodeDocument(contents);
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument));
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var actionParams = new CreateComponentCodeActionParams
{
Uri = documentPath,
@ -132,7 +132,7 @@ public class CreateComponentCodeActionResolverTest : LanguageServerTestBase
var contents = $"@page \"/test\"{Environment.NewLine}@namespace Another.Namespace";
var codeDocument = CreateCodeDocument(contents);
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument));
var resolver = new CreateComponentCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var actionParams = new CreateComponentCodeActionParams
{
Uri = documentPath,

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

@ -39,7 +39,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
public async Task Handle_MissingFile()
{
// Arrange
var resolver = new ExtractToCodeBehindCodeActionResolver(_emptyDocumentContextFactory);
var resolver = new ExtractToCodeBehindCodeActionResolver(_emptyDocumentContextFactory, TestLanguageServerFeatureOptions.Instance);
var data = JObject.FromObject(new ExtractToCodeBehindCodeActionParams()
{
Uri = new Uri("c:/Test.razor"),
@ -66,7 +66,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
var codeDocument = CreateCodeDocument(contents);
codeDocument.SetUnsupported();
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument));
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var data = JObject.FromObject(new ExtractToCodeBehindCodeActionParams()
{
Uri = new Uri("c:/Test.razor"),
@ -93,7 +93,7 @@ public class ExtractToCodeBehindCodeActionResolverTest : LanguageServerTestBase
var codeDocument = CreateCodeDocument(contents);
codeDocument.SetFileKind(FileKinds.Legacy);
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument));
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var data = JObject.FromObject(new ExtractToCodeBehindCodeActionParams()
{
Uri = new Uri("c:/Test.razor"),
@ -120,7 +120,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));
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var actionParams = new ExtractToCodeBehindCodeActionParams
{
Uri = documentPath,
@ -169,7 +169,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));
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var actionParams = new ExtractToCodeBehindCodeActionParams
{
Uri = documentPath,
@ -218,7 +218,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));
var resolver = new ExtractToCodeBehindCodeActionResolver(CreateDocumentContextFactory(documentPath, codeDocument), TestLanguageServerFeatureOptions.Instance);
var actionParams = new ExtractToCodeBehindCodeActionParams
{
Uri = documentPath,

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

@ -47,7 +47,7 @@ public class RenameEndpointTest : LanguageServerTestBase
public async Task Handle_Rename_FileManipulationNotSupported_ReturnsNull()
{
// Arrange
var languageServerFeatureOptions = Mock.Of<LanguageServerFeatureOptions>(options => options.SupportsFileManipulation == false, MockBehavior.Strict);
var languageServerFeatureOptions = Mock.Of<LanguageServerFeatureOptions>(options => options.SupportsFileManipulation == false && options.ReturnCodeActionAndRenamePathsWithPrefixedSlash == false, MockBehavior.Strict);
var endpoint = CreateEndpoint(languageServerFeatureOptions);
var uri = new Uri("file:///c:/First/Component1.razor");
var request = new RenameParamsBridge
@ -421,7 +421,8 @@ public class RenameEndpointTest : LanguageServerTestBase
public async Task Handle_Rename_SingleServer_CallsDelegatedLanguageServer()
{
// Arrange
var languageServerFeatureOptions = Mock.Of<LanguageServerFeatureOptions>(options => options.SupportsFileManipulation == true && options.SingleServerSupport == true, MockBehavior.Strict);
var languageServerFeatureOptions = Mock.Of<LanguageServerFeatureOptions>(
options => options.SupportsFileManipulation == true && options.SingleServerSupport == true && options.ReturnCodeActionAndRenamePathsWithPrefixedSlash == false, MockBehavior.Strict);
var delegatedEdit = new WorkspaceEdit();
@ -469,7 +470,8 @@ public class RenameEndpointTest : LanguageServerTestBase
public async Task Handle_Rename_SingleServer_DoesntDelegateForRazor()
{
// Arrange
var languageServerFeatureOptions = Mock.Of<LanguageServerFeatureOptions>(options => options.SupportsFileManipulation == true && options.SingleServerSupport == true, MockBehavior.Strict);
var languageServerFeatureOptions = Mock.Of<LanguageServerFeatureOptions>(
options => options.SupportsFileManipulation == true && options.SingleServerSupport == true && options.ReturnCodeActionAndRenamePathsWithPrefixedSlash == false, MockBehavior.Strict);
var languageServerMock = new Mock<ClientNotifierServiceBase>(MockBehavior.Strict);
var documentMappingServiceMock = new Mock<RazorDocumentMappingService>(MockBehavior.Strict);
documentMappingServiceMock
@ -640,7 +642,8 @@ public class RenameEndpointTest : LanguageServerTestBase
d.TryCreateAsync(new Uri(itemDirectory2.FilePath), It.IsAny<CancellationToken>()) == Task.FromResult(directory2Component), MockBehavior.Strict);
var searchEngine = new DefaultRazorComponentSearchEngine(Dispatcher, projectSnapshotManagerAccessor, LoggerFactory);
languageServerFeatureOptions ??= Mock.Of<LanguageServerFeatureOptions>(options => options.SupportsFileManipulation == true && options.SingleServerSupport == false, MockBehavior.Strict);
languageServerFeatureOptions ??= Mock.Of<LanguageServerFeatureOptions>(
options => options.SupportsFileManipulation == true && options.SingleServerSupport == false && options.ReturnCodeActionAndRenamePathsWithPrefixedSlash == false, MockBehavior.Strict);
var documentMappingServiceMock = new Mock<RazorDocumentMappingService>(MockBehavior.Strict);
documentMappingServiceMock

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

@ -26,4 +26,6 @@ internal class TestLanguageServerFeatureOptions : LanguageServerFeatureOptions
public override bool SingleServerSupport => false;
public override bool SupportsDelegatedCodeActions => false;
public override bool ReturnCodeActionAndRenamePathsWithPrefixedSlash => false;
}