Skip to content

Commit 33c66f4

Browse files
committed
Some of the PR feedback that wasn't deferred to the next one, and added tests
1 parent 83703f6 commit 33c66f4

File tree

2 files changed

+93
-51
lines changed

2 files changed

+93
-51
lines changed

src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/Razor/ExtractToComponentCodeActionProvider.cs

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,10 @@ private static (SyntaxNode? Start, SyntaxNode? End) FindContainingSiblingPair(Sy
261261
{
262262
var current = node1;
263263

264-
while (current is MarkupElementSyntax && current is not null)
264+
while (current is MarkupElementSyntax or
265+
MarkupTagHelperAttributeSyntax or
266+
MarkupBlockSyntax &&
267+
current is not null)
265268
{
266269
if (current.Span.Contains(node2.Span))
267270
{
@@ -279,51 +282,28 @@ private static void AddComponentDependenciesInRange(SyntaxNode root, int extract
279282
var components = new HashSet<string>();
280283
var extractSpan = new TextSpan(extractStart, extractEnd - extractStart);
281284

282-
foreach (var node in root.DescendantNodes())
285+
foreach (var node in root.DescendantNodes().Where(node => extractSpan.Contains(node.Span)))
283286
{
284-
if (IsMarkupTagHelperElement(node, extractSpan))
287+
if (node is MarkupTagHelperElementSyntax { TagHelperInfo: { } tagHelperInfo })
285288
{
286-
var tagHelperInfo = GetTagHelperInfo(node);
287-
if (tagHelperInfo is not null)
288-
{
289-
AddDependenciesFromTagHelperInfo(tagHelperInfo, components, actionParams);
290-
}
289+
AddDependenciesFromTagHelperInfo(tagHelperInfo, components, actionParams);
291290
}
292291
}
293292
}
294293

295-
private static bool IsMarkupTagHelperElement(SyntaxNode node, TextSpan extractSpan)
296-
{
297-
return node is MarkupTagHelperElementSyntax markupElement &&
298-
extractSpan.Contains(markupElement.Span);
299-
}
300-
301-
private static TagHelperInfo? GetTagHelperInfo(SyntaxNode node)
302-
{
303-
if (node is MarkupTagHelperElementSyntax markupElement)
304-
{
305-
return markupElement.TagHelperInfo;
306-
}
307-
308-
return null;
309-
}
310-
311294
private static void AddDependenciesFromTagHelperInfo(TagHelperInfo tagHelperInfo, HashSet<string> components, ExtractToComponentCodeActionParams actionParams)
312295
{
313296
foreach (var descriptor in tagHelperInfo.BindingResult.Descriptors)
314297
{
315-
if (descriptor is not null)
298+
if (descriptor is null)
299+
{
300+
continue;
301+
}
302+
303+
var typeNamespace = descriptor.GetTypeNamespace();
304+
if (components.Add(typeNamespace))
316305
{
317-
foreach (var metadata in descriptor.Metadata)
318-
{
319-
if (metadata.Key == TagHelperMetadata.Common.TypeNamespace &&
320-
metadata.Value is not null &&
321-
!components.Contains(metadata.Value))
322-
{
323-
components.Add(metadata.Value);
324-
actionParams.Dependencies.Add($"@using {metadata.Value}");
325-
}
326-
}
306+
actionParams.Dependencies.Add($"@using {typeNamespace}");
327307
}
328308
}
329309
}

src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/CodeActionEndToEndTest.NetFx.cs

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,9 @@ private GenerateMethodCodeActionResolver[] CreateRazorCodeActionResolvers(
6161
razorFormattingService)
6262
];
6363

64-
// TODO: Make this func
65-
private ExtractToComponentCodeActionResolver[] CreateExtractComponentCodeActionResolvers(string filePath, RazorCodeDocument codeDocument)
64+
65+
private ExtractToComponentCodeActionResolver[] CreateExtractComponentCodeActionResolver(string filePath, RazorCodeDocument codeDocument)
6666
{
67-
var emptyDocumentContextFactory = new TestDocumentContextFactory();
6867
return [
6968
new ExtractToComponentCodeActionResolver(
7069
new GenerateMethodResolverDocumentContextFactory(filePath, codeDocument),
@@ -1019,7 +1018,7 @@ await ValidateCodeActionAsync(input,
10191018
}
10201019

10211020
[Fact]
1022-
public async Task Handle_ExtractComponent()
1021+
public async Task Handle_ExtractComponent_SingleElement_ReturnsResult()
10231022
{
10241023
var input = """
10251024
<[||]div id="a">
@@ -1045,7 +1044,75 @@ await ValidateExtractComponentCodeActionAsync(
10451044
expectedRazorComponent,
10461045
ExtractToComponentTitle,
10471046
razorCodeActionProviders: [new ExtractToComponentCodeActionProvider(LoggerFactory)],
1048-
codeActionResolversCreator: CreateExtractComponentCodeActionResolvers);
1047+
codeActionResolversCreator: CreateExtractComponentCodeActionResolver);
1048+
}
1049+
1050+
[Fact]
1051+
public async Task Handle_ExtractComponent_SiblingElement_ReturnsResult()
1052+
{
1053+
var input = """
1054+
<[|div id="a">
1055+
<h1>Div a title</h1>
1056+
<Book Title="To Kill a Mockingbird" Author="Harper Lee" Year="Long ago" />
1057+
<p>Div a par</p>
1058+
</div>
1059+
<div id="b">
1060+
<Movie Title="Aftersun" Director="Charlotte Wells" Year="2022" />
1061+
</div|]>
1062+
""";
1063+
1064+
var expectedRazorComponent = """
1065+
<div id="a">
1066+
<h1>Div a title</h1>
1067+
<Book Title="To Kill a Mockingbird" Author="Harper Lee" Year="Long ago" />
1068+
<p>Div a par</p>
1069+
</div>
1070+
<div id="b">
1071+
<Movie Title="Aftersun" Director="Charlotte Wells" Year="2022" />
1072+
</div>
1073+
""";
1074+
1075+
await ValidateExtractComponentCodeActionAsync(
1076+
input,
1077+
expectedRazorComponent,
1078+
ExtractToComponentTitle,
1079+
razorCodeActionProviders: [new ExtractToComponentCodeActionProvider(LoggerFactory)],
1080+
codeActionResolversCreator: CreateExtractComponentCodeActionResolver);
1081+
}
1082+
1083+
[Fact]
1084+
public async Task Handle_ExtractComponent_StartNodeContainsEndNode_ReturnsResult()
1085+
{
1086+
var input = """
1087+
<[|div id="parent">
1088+
<div>
1089+
<div>
1090+
<div>
1091+
<p>Deeply nested par</p|]>
1092+
</div>
1093+
</div>
1094+
</div>
1095+
</div>
1096+
""";
1097+
1098+
var expectedRazorComponent = """
1099+
<div id="parent">
1100+
<div>
1101+
<div>
1102+
<div>
1103+
<p>Deeply nested par</p>
1104+
</div>
1105+
</div>
1106+
</div>
1107+
</div>
1108+
""";
1109+
1110+
await ValidateExtractComponentCodeActionAsync(
1111+
input,
1112+
expectedRazorComponent,
1113+
ExtractToComponentTitle,
1114+
razorCodeActionProviders: [new ExtractToComponentCodeActionProvider(LoggerFactory)],
1115+
codeActionResolversCreator: CreateExtractComponentCodeActionResolver);
10491116
}
10501117

10511118
#endregion
@@ -1196,19 +1263,20 @@ private async Task ValidateExtractComponentCodeActionAsync(
11961263
string? expected,
11971264
string codeAction,
11981265
int childActionIndex = 0,
1266+
IEnumerable<(string filePath, string contents)>? additionalRazorDocuments = null,
11991267
IRazorCodeActionProvider[]? razorCodeActionProviders = null,
12001268
Func<string, RazorCodeDocument, IRazorCodeActionResolver[]>? codeActionResolversCreator = null,
12011269
RazorLSPOptionsMonitor? optionsMonitor = null,
12021270
Diagnostic[]? diagnostics = null)
12031271
{
12041272
TestFileMarkupParser.GetSpan(input, out input, out var textSpan);
12051273

1206-
var razorFilePath = "C:/path/test.razor";
1207-
var componentFilePath = "C:/path/Component.razor";
1274+
var razorFilePath = "C:/path/to/test.razor";
1275+
var componentFilePath = "C:/path/to/Component.razor";
12081276
var codeDocument = CreateCodeDocument(input, filePath: razorFilePath);
12091277
var sourceText = codeDocument.Source.Text;
12101278
var uri = new Uri(razorFilePath);
1211-
var languageServer = await CreateLanguageServerAsync(codeDocument, razorFilePath);
1279+
var languageServer = await CreateLanguageServerAsync(codeDocument, razorFilePath, additionalRazorDocuments);
12121280
var documentContext = CreateDocumentContext(uri, codeDocument);
12131281
var requestContext = new RazorRequestContext(documentContext, null!, "lsp/method", uri: null);
12141282

@@ -1239,15 +1307,9 @@ private async Task ValidateExtractComponentCodeActionAsync(
12391307
languageServer,
12401308
codeActionResolversCreator?.Invoke(razorFilePath, codeDocument) ?? []);
12411309

1242-
var edits = new List<TextChange>();
1243-
1244-
// Only get changes made in the new component file
1245-
foreach (var change in changes.Where(e => e.TextDocument.Uri.AbsolutePath == componentFilePath))
1246-
{
1247-
edits.AddRange(change.Edits.Select(sourceText.GetTextChange));
1248-
}
1310+
var edits = changes.Where(change => change.TextDocument.Uri.AbsolutePath == componentFilePath).Single();
1311+
var actual = edits.Edits.Select(edit => edit.NewText).Single();
12491312

1250-
var actual = sourceText.WithChanges(edits).ToString();
12511313
AssertEx.EqualOrDiff(expected, actual);
12521314
}
12531315

0 commit comments

Comments
 (0)