1- // Licensed to the .NET Foundation under one or more agreements.
1+ // Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System . Collections . Immutable ;
1010using Microsoft . AspNetCore . Razor . Threading ;
1111using Microsoft . CodeAnalysis . Razor . CodeActions . Models ;
1212using Microsoft . CodeAnalysis . Razor . CodeActions . Razor ;
13+ using Microsoft . CodeAnalysis . Razor . Workspaces ;
1314
1415namespace Microsoft . CodeAnalysis . Razor . CodeActions ;
1516
@@ -48,20 +49,8 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
4849 return SpecializedTasks . EmptyImmutableArray < RazorVSInternalCodeAction > ( ) ;
4950 }
5051
51- // Get the attribute name - it includes the '@' prefix for directive attributes
52- var attributeName = attributeBlock . Name . GetContent ( ) ;
53-
54- // Check if this is a directive attribute (starts with '@')
55- if ( string . IsNullOrEmpty ( attributeName ) || ! attributeName . StartsWith ( "@" ) )
56- {
57- return SpecializedTasks . EmptyImmutableArray < RazorVSInternalCodeAction > ( ) ;
58- }
59-
6052 // Try to find the missing namespace for this directive attribute
61- if ( ! TryGetMissingDirectiveAttributeNamespace (
62- context . CodeDocument ,
63- attributeName ,
64- out var missingNamespace ) )
53+ if ( ! TryGetMissingDirectiveAttributeNamespace ( context . CodeDocument , attributeBlock , out var missingNamespace ) )
6554 {
6655 return SpecializedTasks . EmptyImmutableArray < RazorVSInternalCodeAction > ( ) ;
6756 }
@@ -78,20 +67,23 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
7867 newTagName : null ,
7968 resolutionParams ) ;
8069
81- // Set high priority and order to show prominently
82- addUsingCodeAction . Priority = VSInternalPriorityLevel . High ;
83- addUsingCodeAction . Order = - 999 ;
84-
8570 return Task . FromResult < ImmutableArray < RazorVSInternalCodeAction > > ( [ addUsingCodeAction ] ) ;
8671 }
8772
8873 private static bool TryGetMissingDirectiveAttributeNamespace (
8974 RazorCodeDocument codeDocument ,
90- string attributeName ,
75+ MarkupAttributeBlockSyntax attributeBlock ,
9176 [ NotNullWhen ( true ) ] out string ? missingNamespace )
9277 {
9378 missingNamespace = null ;
9479
80+ // Check if this is a directive attribute (starts with '@')
81+ var attributeName = attributeBlock . Name . GetContent ( ) ;
82+ if ( attributeName is not [ '@' , ..] )
83+ {
84+ return false ;
85+ }
86+
9587 // Get all tag helpers, not just those in scope, since we want to suggest adding a using
9688 var tagHelpers = codeDocument . GetTagHelpers ( ) ;
9789 if ( tagHelpers is null )
@@ -110,36 +102,26 @@ private static bool TryGetMissingDirectiveAttributeNamespace(
110102 // Search for matching bound attribute descriptors in all available tag helpers
111103 foreach ( var tagHelper in tagHelpers )
112104 {
105+ if ( ! tagHelper . IsAttributeDescriptor ( ) )
106+ {
107+ continue ;
108+ }
109+
113110 foreach ( var boundAttribute in tagHelper . BoundAttributes )
114111 {
115- if ( boundAttribute . Name == baseAttributeName )
112+ // No need to worry about multiple matches, because Razor syntax has no way to disambiguate anyway.
113+ // Currently only compiler can create directive attribute tag helpers anyway.
114+ if ( boundAttribute . IsDirectiveAttribute &&
115+ boundAttribute . Name == baseAttributeName )
116116 {
117- // Extract namespace from the type name
118- var typeName = boundAttribute . TypeName ;
119-
120- // Apply heuristics to determine the namespace
121- // Check for Web namespace indicators (event args types are defined there)
122- if ( typeName . Contains ( ".Web." ) || typeName . Contains ( ".Web>" ) ||
123- typeName . Contains ( "EventArgs" ) || typeName . Contains ( "EventCallback" ) )
117+ if ( boundAttribute . Parent . TypeNamespace is { } typeNamespace )
124118 {
125- missingNamespace = "Microsoft.AspNetCore.Components.Web" ;
119+ missingNamespace = typeNamespace ;
126120 return true ;
127121 }
128- else if ( typeName . Contains ( ".Forms." ) || typeName . Contains ( ".Forms>" ) )
129- {
130- missingNamespace = "Microsoft.AspNetCore.Components.Forms" ;
131- return true ;
132- }
133- else
134- {
135- // Extract namespace from type name using the existing method
136- var extractedNamespace = AddUsingsCodeActionResolver . GetNamespaceFromFQN ( typeName ) ;
137- if ( ! string . IsNullOrEmpty ( extractedNamespace ) )
138- {
139- missingNamespace = extractedNamespace ;
140- return true ;
141- }
142- }
122+
123+ // This is unexpected, but if for some reason we can't find a namespace, there is no point looking further
124+ break ;
143125 }
144126 }
145127 }
0 commit comments