Skip to content

Commit 38d9cb0

Browse files
Copilotdavidwengier
andcommitted
Address code review feedback - extract method, use GetTagHelpers(), keep '@' in names
- Extract CreateAddUsingResolutionParams method to avoid creating fake FQN - Use codeDocument.GetTagHelpers() instead of GetRequiredTagHelperContext() to get all tag helpers - Don't strip '@' from attribute names - descriptor names include it - Add test for @Bind:after scenario (attribute with parameter) Tests improved from 3/8 to 5/9 passing. @OnClick and @onchange tests now pass. @Bind tests still failing - need to investigate why @Bind descriptors aren't matching. Co-authored-by: davidwengier <[email protected]>
1 parent 345c10d commit 38d9cb0

File tree

3 files changed

+44
-33
lines changed

3 files changed

+44
-33
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,22 +62,26 @@ internal static bool TryCreateAddUsingResolutionParams(string fullyQualifiedName
6262
return false;
6363
}
6464

65+
resolutionParams = CreateAddUsingResolutionParams(@namespace, textDocument, additionalEdit, delegatedDocumentUri);
66+
return true;
67+
}
68+
69+
internal static RazorCodeActionResolutionParams CreateAddUsingResolutionParams(string @namespace, VSTextDocumentIdentifier textDocument, TextDocumentEdit? additionalEdit, Uri? delegatedDocumentUri)
70+
{
6571
var actionParams = new AddUsingsCodeActionParams
6672
{
6773
Namespace = @namespace,
6874
AdditionalEdit = additionalEdit
6975
};
7076

71-
resolutionParams = new RazorCodeActionResolutionParams
77+
return new RazorCodeActionResolutionParams
7278
{
7379
TextDocument = textDocument,
7480
Action = LanguageServerConstants.CodeActions.AddUsing,
7581
Language = RazorLanguageKind.Razor,
7682
DelegatedDocumentUri = delegatedDocumentUri,
7783
Data = actionParams,
7884
};
79-
80-
return true;
8185
}
8286

8387
// Internal for testing

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

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -67,30 +67,22 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
6767
}
6868

6969
// Create the code action
70-
// We need to pass a fully qualified name to TryCreateAddUsingResolutionParams,
71-
// which will extract the namespace. We append a dummy type name since the method
72-
// expects a format like "Namespace.TypeName" and extracts everything before the last dot.
73-
if (AddUsingsCodeActionResolver.TryCreateAddUsingResolutionParams(
74-
missingNamespace + ".Component", // Append dummy type name for namespace extraction
70+
var resolutionParams = AddUsingsCodeActionResolver.CreateAddUsingResolutionParams(
71+
missingNamespace,
7572
context.Request.TextDocument,
7673
additionalEdit: null,
77-
context.DelegatedDocumentUri,
78-
out var extractedNamespace,
79-
out var resolutionParams))
80-
{
81-
var addUsingCodeAction = RazorCodeActionFactory.CreateAddComponentUsing(
82-
extractedNamespace,
83-
newTagName: null,
84-
resolutionParams);
74+
context.DelegatedDocumentUri);
8575

86-
// Set high priority and order to show prominently
87-
addUsingCodeAction.Priority = VSInternalPriorityLevel.High;
88-
addUsingCodeAction.Order = -999;
76+
var addUsingCodeAction = RazorCodeActionFactory.CreateAddComponentUsing(
77+
missingNamespace,
78+
newTagName: null,
79+
resolutionParams);
8980

90-
return Task.FromResult<ImmutableArray<RazorVSInternalCodeAction>>([addUsingCodeAction]);
91-
}
81+
// Set high priority and order to show prominently
82+
addUsingCodeAction.Priority = VSInternalPriorityLevel.High;
83+
addUsingCodeAction.Order = -999;
9284

93-
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
85+
return Task.FromResult<ImmutableArray<RazorVSInternalCodeAction>>([addUsingCodeAction]);
9486
}
9587

9688
private static bool TryGetMissingDirectiveAttributeNamespace(
@@ -100,23 +92,23 @@ private static bool TryGetMissingDirectiveAttributeNamespace(
10092
{
10193
missingNamespace = null;
10294

103-
var tagHelperContext = codeDocument.GetRequiredTagHelperContext();
104-
105-
// Remove the '@' prefix for matching against tag helper descriptors
106-
// The attribute name from syntax is "@onclick" but descriptors use "onclick"
107-
var nameWithoutAt = attributeName.StartsWith("@") ? attributeName[1..] : attributeName;
95+
// Get all tag helpers, not just those in scope, since we want to suggest adding a using
96+
var tagHelpers = codeDocument.GetTagHelpers();
97+
if (tagHelpers is null)
98+
{
99+
return false;
100+
}
108101

109-
// For attributes with parameters (e.g., @bind:after becomes bind:after then bind),
110-
// extract just the base attribute name
111-
var baseAttributeName = nameWithoutAt;
112-
var colonIndex = nameWithoutAt.IndexOf(':');
102+
// For attributes with parameters (e.g., @bind:after), extract just the base attribute name
103+
var baseAttributeName = attributeName;
104+
var colonIndex = attributeName.IndexOf(':');
113105
if (colonIndex > 0)
114106
{
115-
baseAttributeName = nameWithoutAt[..colonIndex];
107+
baseAttributeName = attributeName[..colonIndex];
116108
}
117109

118110
// Search for matching bound attribute descriptors in all available tag helpers
119-
foreach (var tagHelper in tagHelperContext.TagHelpers)
111+
foreach (var tagHelper in tagHelpers)
120112
{
121113
foreach (var boundAttribute in tagHelper.BoundAttributes)
122114
{

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,19 @@ @using Microsoft.AspNetCore.Components.Web
122122

123123
await VerifyCodeActionAsync(input, expected, LanguageServerConstants.CodeActions.AddUsing, addDefaultImports: false);
124124
}
125+
126+
[Fact]
127+
public async Task AddUsing_BindWithParameter()
128+
{
129+
var input = """
130+
<input @bind:[||]after="HandleAfter" />
131+
""";
132+
133+
var expected = """
134+
@using Microsoft.AspNetCore.Components.Web
135+
<input @bind:after="HandleAfter" />
136+
""";
137+
138+
await VerifyCodeActionAsync(input, expected, LanguageServerConstants.CodeActions.AddUsing, addDefaultImports: false);
139+
}
125140
}

0 commit comments

Comments
 (0)