Skip to content

Commit 3d9614c

Browse files
committed
Exclude usings that are already in _Imports.razor
1 parent 5b7e0c6 commit 3d9614c

File tree

1 file changed

+44
-7
lines changed

1 file changed

+44
-7
lines changed

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

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Collections.Immutable;
77
using System.Diagnostics.CodeAnalysis;
8+
using System.IO;
89
using System.Linq;
910
using System.Text;
1011
using System.Threading;
@@ -16,8 +17,11 @@
1617
using Microsoft.AspNetCore.Razor.Language.Intermediate;
1718
using Microsoft.AspNetCore.Razor.Language.Syntax;
1819
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions.Models;
20+
using Microsoft.AspNetCore.Razor.PooledObjects;
1921
using Microsoft.AspNetCore.Razor.Threading;
22+
using Microsoft.AspNetCore.Razor.Utilities;
2023
using Microsoft.CodeAnalysis.CSharp.Syntax;
24+
using Microsoft.CodeAnalysis.Razor;
2125
using Microsoft.CodeAnalysis.Razor.Logging;
2226
using Microsoft.CodeAnalysis.Razor.Workspaces;
2327
using Microsoft.CodeAnalysis.Text;
@@ -71,13 +75,19 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
7175

7276
var actionParams = CreateInitialActionParams(context, startElementNode, @namespace);
7377

78+
var path = FilePathNormalizer.Normalize(actionParams.Uri.GetAbsoluteOrUNCPath());
79+
var directoryName = Path.GetDirectoryName(path).AssumeNotNull();
80+
var importsBuilder = new StringBuilder();
81+
var usingsInImportsFile = GetUsingsInImportsFile(directoryName, importsBuilder);
82+
7483
ProcessSelection(startElementNode, endElementNode, actionParams);
7584

7685
var utilityScanRoot = FindNearestCommonAncestor(startElementNode, endElementNode) ?? startElementNode;
7786
AddUsingDirectivesInRange(utilityScanRoot,
78-
actionParams.ExtractStart,
79-
actionParams.ExtractEnd,
80-
actionParams);
87+
usingsInImportsFile,
88+
actionParams.ExtractStart,
89+
actionParams.ExtractEnd,
90+
actionParams);
8191

8292
var resolutionParams = new RazorCodeActionResolutionParams()
8393
{
@@ -159,6 +169,29 @@ private static ExtractToComponentCodeActionParams CreateInitialActionParams(Razo
159169
};
160170
}
161171

172+
// When adding usings, we must check for the first _Imports.razor file in the current directory or any parent directories.
173+
// In the new component, only add usings that are not already present in the _Imports.razor file.
174+
public static string GetUsingsInImportsFile(string currentDirectory, StringBuilder importsBuilder)
175+
{
176+
var importsFile = Path.Combine(currentDirectory, "_Imports.razor");
177+
if (File.Exists(importsFile))
178+
{
179+
return File.ReadAllLines(importsFile)
180+
.Where(line => line.TrimStart().StartsWith("@using"))
181+
.Select(line => line.Trim())
182+
.Aggregate(importsBuilder, (sb, line) => sb.AppendLine(line))
183+
.ToString();
184+
}
185+
186+
var parentDirectory = Path.GetDirectoryName(currentDirectory);
187+
if (parentDirectory is not null && Directory.Exists(parentDirectory))
188+
{
189+
return GetUsingsInImportsFile(parentDirectory, importsBuilder);
190+
}
191+
192+
return string.Empty;
193+
}
194+
162195
/// <summary>
163196
/// Processes a multi-point selection to determine the correct range for extraction.
164197
/// </summary>
@@ -274,7 +307,7 @@ MarkupTagHelperAttributeSyntax or
274307
return null;
275308
}
276309

277-
private static void AddUsingDirectivesInRange(SyntaxNode root, int extractStart, int extractEnd, ExtractToComponentCodeActionParams actionParams)
310+
private static void AddUsingDirectivesInRange(SyntaxNode root, string usingsInImportsFile, int extractStart, int extractEnd, ExtractToComponentCodeActionParams actionParams)
278311
{
279312
var components = new HashSet<string>();
280313
var extractSpan = new TextSpan(extractStart, extractEnd - extractStart);
@@ -283,12 +316,12 @@ private static void AddUsingDirectivesInRange(SyntaxNode root, int extractStart,
283316
{
284317
if (node is MarkupTagHelperElementSyntax { TagHelperInfo: { } tagHelperInfo })
285318
{
286-
AddUsingFromTagHelperInfo(tagHelperInfo, components, actionParams);
319+
AddUsingFromTagHelperInfo(tagHelperInfo, components, usingsInImportsFile, actionParams);
287320
}
288321
}
289322
}
290323

291-
private static void AddUsingFromTagHelperInfo(TagHelperInfo tagHelperInfo, HashSet<string> components, ExtractToComponentCodeActionParams actionParams)
324+
private static void AddUsingFromTagHelperInfo(TagHelperInfo tagHelperInfo, HashSet<string> components, string usingsInImportsFile, ExtractToComponentCodeActionParams actionParams)
292325
{
293326
foreach (var descriptor in tagHelperInfo.BindingResult.Descriptors)
294327
{
@@ -300,7 +333,11 @@ private static void AddUsingFromTagHelperInfo(TagHelperInfo tagHelperInfo, HashS
300333
var typeNamespace = descriptor.GetTypeNamespace();
301334
if (components.Add(typeNamespace))
302335
{
303-
actionParams.usingDirectives.Add($"@using {typeNamespace}");
336+
var usingDirective = $"@using {typeNamespace}";
337+
if (!usingsInImportsFile.Contains(usingDirective))
338+
{
339+
actionParams.usingDirectives.Add(usingDirective);
340+
}
304341
}
305342
}
306343
}

0 commit comments

Comments
 (0)