5
5
using System . Collections . Generic ;
6
6
using System . Collections . Immutable ;
7
7
using System . Diagnostics . CodeAnalysis ;
8
+ using System . IO ;
8
9
using System . Linq ;
9
10
using System . Text ;
10
11
using System . Threading ;
16
17
using Microsoft . AspNetCore . Razor . Language . Intermediate ;
17
18
using Microsoft . AspNetCore . Razor . Language . Syntax ;
18
19
using Microsoft . AspNetCore . Razor . LanguageServer . CodeActions . Models ;
20
+ using Microsoft . AspNetCore . Razor . PooledObjects ;
19
21
using Microsoft . AspNetCore . Razor . Threading ;
22
+ using Microsoft . AspNetCore . Razor . Utilities ;
20
23
using Microsoft . CodeAnalysis . CSharp . Syntax ;
24
+ using Microsoft . CodeAnalysis . Razor ;
21
25
using Microsoft . CodeAnalysis . Razor . Logging ;
22
26
using Microsoft . CodeAnalysis . Razor . Workspaces ;
23
27
using Microsoft . CodeAnalysis . Text ;
@@ -71,13 +75,19 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
71
75
72
76
var actionParams = CreateInitialActionParams ( context , startElementNode , @namespace ) ;
73
77
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
+
74
83
ProcessSelection ( startElementNode , endElementNode , actionParams ) ;
75
84
76
85
var utilityScanRoot = FindNearestCommonAncestor ( startElementNode , endElementNode ) ?? startElementNode ;
77
86
AddUsingDirectivesInRange ( utilityScanRoot ,
78
- actionParams . ExtractStart ,
79
- actionParams . ExtractEnd ,
80
- actionParams ) ;
87
+ usingsInImportsFile ,
88
+ actionParams . ExtractStart ,
89
+ actionParams . ExtractEnd ,
90
+ actionParams ) ;
81
91
82
92
var resolutionParams = new RazorCodeActionResolutionParams ( )
83
93
{
@@ -159,6 +169,29 @@ private static ExtractToComponentCodeActionParams CreateInitialActionParams(Razo
159
169
} ;
160
170
}
161
171
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
+
162
195
/// <summary>
163
196
/// Processes a multi-point selection to determine the correct range for extraction.
164
197
/// </summary>
@@ -274,7 +307,7 @@ MarkupTagHelperAttributeSyntax or
274
307
return null ;
275
308
}
276
309
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 )
278
311
{
279
312
var components = new HashSet < string > ( ) ;
280
313
var extractSpan = new TextSpan ( extractStart , extractEnd - extractStart ) ;
@@ -283,12 +316,12 @@ private static void AddUsingDirectivesInRange(SyntaxNode root, int extractStart,
283
316
{
284
317
if ( node is MarkupTagHelperElementSyntax { TagHelperInfo : { } tagHelperInfo } )
285
318
{
286
- AddUsingFromTagHelperInfo ( tagHelperInfo , components , actionParams ) ;
319
+ AddUsingFromTagHelperInfo ( tagHelperInfo , components , usingsInImportsFile , actionParams ) ;
287
320
}
288
321
}
289
322
}
290
323
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 )
292
325
{
293
326
foreach ( var descriptor in tagHelperInfo . BindingResult . Descriptors )
294
327
{
@@ -300,7 +333,11 @@ private static void AddUsingFromTagHelperInfo(TagHelperInfo tagHelperInfo, HashS
300
333
var typeNamespace = descriptor . GetTypeNamespace ( ) ;
301
334
if ( components . Add ( typeNamespace ) )
302
335
{
303
- actionParams . usingDirectives . Add ( $ "@using { typeNamespace } ") ;
336
+ var usingDirective = $ "@using { typeNamespace } ";
337
+ if ( ! usingsInImportsFile . Contains ( usingDirective ) )
338
+ {
339
+ actionParams . usingDirectives . Add ( usingDirective ) ;
340
+ }
304
341
}
305
342
}
306
343
}
0 commit comments