зеркало из https://github.com/dotnet/razor.git
Support component rename from an end tag (#10762)
Fixes https://github.com/dotnet/razor/issues/10717
This commit is contained in:
Коммит
e16582150b
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
@ -250,23 +251,7 @@ internal class RenameService(
|
|||
return default;
|
||||
}
|
||||
|
||||
var node = owner.FirstAncestorOrSelf<RazorSyntaxNode>(n => n.Kind == RazorSyntaxKind.MarkupTagHelperStartTag);
|
||||
if (node is not MarkupTagHelperStartTagSyntax tagHelperStartTag)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
// Ensure the rename action was invoked on the component name
|
||||
// instead of a component parameter. This serves as an issue
|
||||
// mitigation till `textDocument/prepareRename` is supported
|
||||
// and we can ensure renames aren't triggered in unsupported
|
||||
// contexts. (https://github.com/dotnet/aspnetcore/issues/26407)
|
||||
if (!tagHelperStartTag.Name.FullSpan.IntersectsWith(absoluteIndex))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (tagHelperStartTag.Parent is not MarkupTagHelperElementSyntax { TagHelperInfo.BindingResult: var binding })
|
||||
if (!TryGetTagHelperBinding(owner, absoluteIndex, out var binding))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
@ -288,6 +273,43 @@ internal class RenameService(
|
|||
return [primaryTagHelper, associatedTagHelper];
|
||||
}
|
||||
|
||||
private static bool TryGetTagHelperBinding(RazorSyntaxNode owner, int absoluteIndex, [NotNullWhen(true)] out TagHelperBinding? binding)
|
||||
{
|
||||
// End tags are easy, because there is only one possible binding result
|
||||
if (owner is MarkupTagHelperEndTagSyntax { Parent: MarkupTagHelperElementSyntax { TagHelperInfo.BindingResult: var endTagBindingResult } })
|
||||
{
|
||||
binding = endTagBindingResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
// A rename of a start tag could have an "owner" of one of its attributes, so we do a bit more checking
|
||||
// to support this case
|
||||
var node = owner.FirstAncestorOrSelf<RazorSyntaxNode>(n => n.Kind == RazorSyntaxKind.MarkupTagHelperStartTag);
|
||||
if (node is not MarkupTagHelperStartTagSyntax tagHelperStartTag)
|
||||
{
|
||||
binding = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the rename action was invoked on the component name instead of a component parameter. This serves as an issue
|
||||
// mitigation till `textDocument/prepareRename` is supported and we can ensure renames aren't triggered in unsupported
|
||||
// contexts. (https://github.com/dotnet/razor/issues/4285)
|
||||
if (!tagHelperStartTag.Name.FullSpan.IntersectsWith(absoluteIndex))
|
||||
{
|
||||
binding = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tagHelperStartTag is { Parent: MarkupTagHelperElementSyntax { TagHelperInfo.BindingResult: var startTagBindingResult } })
|
||||
{
|
||||
binding = startTagBindingResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
binding = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor? FindAssociatedTagHelper(TagHelperDescriptor tagHelper, ImmutableArray<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
var typeName = tagHelper.GetTypeName();
|
||||
|
|
|
@ -265,6 +265,29 @@ public class RenameEndpointTest(ITestOutputHelper testOutput) : LanguageServerTe
|
|||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_Rename_OnComponentEndTag_ReturnsResult()
|
||||
{
|
||||
// Arrange
|
||||
var (endpoint, documentContextFactory) = await CreateEndpointAndDocumentContextFactoryAsync();
|
||||
var uri = PathUtilities.GetUri(s_componentWithParamFilePath);
|
||||
var request = new RenameParams
|
||||
{
|
||||
TextDocument = new() { Uri = uri },
|
||||
Position = VsLspFactory.CreatePosition(1, 36),
|
||||
NewName = "Test2"
|
||||
};
|
||||
|
||||
Assert.True(documentContextFactory.TryCreateForOpenDocument(uri, out var documentContext));
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
// Act
|
||||
var result = await endpoint.HandleRequestAsync(request, requestContext, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Handle_Rename_OnComponentNameTrailingEdge_ReturnsResult()
|
||||
{
|
||||
|
|
|
@ -112,62 +112,62 @@ public class CohostRenameEndpointTest(ITestOutputHelper testOutputHelper) : Coho
|
|||
""",
|
||||
renames: [("Component.razor", "DifferentName.razor")]);
|
||||
|
||||
[Theory(Skip = "https://github.com/dotnet/razor/issues/10717")]
|
||||
[Theory]
|
||||
[InlineData("$$Component")]
|
||||
[InlineData("Com$$ponent")]
|
||||
[InlineData("Component$$")]
|
||||
public Task Component_EndTag(string endTag)
|
||||
=> VerifyRenamesAsync(
|
||||
input: $"""
|
||||
This is a Razor document.
|
||||
=> VerifyRenamesAsync(
|
||||
input: $"""
|
||||
This is a Razor document.
|
||||
|
||||
<Component />
|
||||
|
||||
<div>
|
||||
<Component />
|
||||
<Component>
|
||||
</Component>
|
||||
|
||||
<div>
|
||||
<Component />
|
||||
<Component>
|
||||
</{endTag}>
|
||||
</Component>
|
||||
<div>
|
||||
<Component />
|
||||
<Component>
|
||||
</{endTag}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
The end.
|
||||
""",
|
||||
additionalFiles: [
|
||||
// The source generator isn't hooked up to our test project, so we have to manually "compile" the razor file
|
||||
(File("Component.cs"), """
|
||||
namespace SomeProject;
|
||||
The end.
|
||||
""",
|
||||
additionalFiles: [
|
||||
// The source generator isn't hooked up to our test project, so we have to manually "compile" the razor file
|
||||
(File("Component.cs"), """
|
||||
namespace SomeProject;
|
||||
|
||||
public class Component : Microsoft.AspNetCore.Components.ComponentBase
|
||||
{
|
||||
}
|
||||
"""),
|
||||
// The above will make the component exist, but the .razor file needs to exist too for Uri presentation
|
||||
(File("Component.razor"), "")
|
||||
],
|
||||
newName: "DifferentName",
|
||||
expected: """
|
||||
This is a Razor document.
|
||||
public class Component : Microsoft.AspNetCore.Components.ComponentBase
|
||||
{
|
||||
}
|
||||
"""),
|
||||
// The above will make the component exist, but the .razor file needs to exist too for Uri presentation
|
||||
(File("Component.razor"), "")
|
||||
],
|
||||
newName: "DifferentName",
|
||||
expected: """
|
||||
This is a Razor document.
|
||||
|
||||
<DifferentName />
|
||||
|
||||
<div>
|
||||
<DifferentName />
|
||||
<DifferentName>
|
||||
</DifferentName>
|
||||
|
||||
<div>
|
||||
<DifferentName />
|
||||
<DifferentName>
|
||||
</DifferentName>
|
||||
<div>
|
||||
<DifferentName />
|
||||
<DifferentName>
|
||||
</DifferentName>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
The end.
|
||||
""",
|
||||
renames: [("Component.razor", "DifferentName.razor")]);
|
||||
The end.
|
||||
""",
|
||||
renames: [("Component.razor", "DifferentName.razor")]);
|
||||
|
||||
[Fact]
|
||||
public Task Mvc()
|
||||
|
|
Загрузка…
Ссылка в новой задаче