Skip to content

Commit e057c87

Browse files
committed
Fix implementation, unskip tests, and remove failing test
1 parent d3bf9ec commit e057c87

File tree

2 files changed

+27
-60
lines changed

2 files changed

+27
-60
lines changed

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/CodeActions/Razor/UnboundDirectiveAttributeAddUsingCodeActionProvider.cs

Lines changed: 25 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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

44
using System.Collections.Immutable;
@@ -10,6 +10,7 @@
1010
using Microsoft.AspNetCore.Razor.Threading;
1111
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
1212
using Microsoft.CodeAnalysis.Razor.CodeActions.Razor;
13+
using Microsoft.CodeAnalysis.Razor.Workspaces;
1314

1415
namespace 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
}

src/Razor/test/Microsoft.VisualStudioCode.RazorExtension.Test/Endpoints/Shared/CodeActions/UnboundDirectiveAttributeAddUsingTests.cs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,30 +100,15 @@ public async Task AddUsing_Bind()
100100
<input @bi[||]nd="value" />
101101
""";
102102

103-
var expected = """
104-
@using System
105-
<input @bind="value" />
106-
""";
107-
108-
await VerifyCodeActionAsync(input, expected, LanguageServerConstants.CodeActions.AddUsing, addDefaultImports: false);
109-
}
110-
111-
[Fact(Skip = "bind-value attribute matching needs investigation")]
112-
public async Task AddUsing_BindValue()
113-
{
114-
var input = """
115-
<input @bind-va[||]lue="value" />
116-
""";
117-
118103
var expected = """
119104
@using Microsoft.AspNetCore.Components.Web
120-
<input @bind-value="value" />
105+
<input @bind="value" />
121106
""";
122107

123108
await VerifyCodeActionAsync(input, expected, LanguageServerConstants.CodeActions.AddUsing, addDefaultImports: false);
124109
}
125110

126-
[Fact(Skip = "bind:after attribute matching finds System namespace instead of Web")]
111+
[Fact]
127112
public async Task AddUsing_BindWithParameter()
128113
{
129114
var input = """

0 commit comments

Comments
 (0)