Skip to content

Commit b9e53af

Browse files
authored
Make completion capabilities checks more robust (#11964)
Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2508507 I accidentally broke Html completion when I fixed override completion in VS Code. Previously we would always wrap the Data object, I made it only wrap if the delegated server specified it, but Html (in VS code at least?) doesn't specify any data, which meant we couldn't work out what document the request was for. This fixes it by correctly being guided by the client capabilities as to which data object should be set.
2 parents aa1041b + e38fb89 commit b9e53af

File tree

13 files changed

+104
-70
lines changed

13 files changed

+104
-70
lines changed

src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionListProvider.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,9 @@ internal class DelegatedCompletionListProvider(
116116
? DelegatedCompletionHelper.RewriteCSharpResponse(delegatedResponse, absoluteIndex, codeDocument, positionInfo.Position, razorCompletionOptions)
117117
: DelegatedCompletionHelper.RewriteHtmlResponse(delegatedResponse, razorCompletionOptions);
118118

119-
var completionCapability = clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
120119
var resolutionContext = new DelegatedCompletionResolutionContext(identifier, positionInfo.LanguageKind, rewrittenResponse.Data ?? rewrittenResponse.ItemDefaults?.Data);
121120
var resultId = _completionListCache.Add(rewrittenResponse, resolutionContext);
122-
rewrittenResponse.SetResultId(resultId, completionCapability);
121+
rewrittenResponse.SetResultId(resultId, clientCapabilities);
123122

124123
return rewrittenResponse;
125124
}

src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(CompletionParams request
9393
return null;
9494
}
9595

96-
var completionCapability = _clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
97-
var supportsCompletionListData = completionCapability.SupportsCompletionListData();
98-
99-
RazorCompletionResolveData.Wrap(result, request.TextDocument, supportsCompletionListData: supportsCompletionListData);
96+
RazorCompletionResolveData.Wrap(result, request.TextDocument, _clientCapabilities);
10097
return result;
10198
}
10299
}

src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/Completion/CohostDocumentCompletionEndpoint.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,7 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
211211
return null;
212212
}
213213

214-
var completionCapability = _clientCapabilitiesService.ClientCapabilities.TextDocument?.Completion as VSInternalCompletionSetting;
215-
var supportsCompletionListData = completionCapability.SupportsCompletionListData();
216-
217-
RazorCompletionResolveData.Wrap(combinedCompletionList, originalTextDocumentIdentifier, supportsCompletionListData: supportsCompletionListData);
214+
RazorCompletionResolveData.Wrap(combinedCompletionList, originalTextDocumentIdentifier, _clientCapabilitiesService.ClientCapabilities);
218215

219216
return combinedCompletionList;
220217
}
@@ -236,12 +233,10 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
236233

237234
var rewrittenResponse = DelegatedCompletionHelper.RewriteHtmlResponse(result, razorCompletionOptions);
238235

239-
var completionCapability = _clientCapabilitiesService.ClientCapabilities.TextDocument?.Completion as VSInternalCompletionSetting;
240-
241236
var razorDocumentIdentifier = new TextDocumentIdentifierAndVersion(request.TextDocument, Version: 0);
242237
var resolutionContext = new DelegatedCompletionResolutionContext(razorDocumentIdentifier, RazorLanguageKind.Html, rewrittenResponse.Data ?? rewrittenResponse.ItemDefaults?.Data);
243238
var resultId = _completionListCache.Add(rewrittenResponse, resolutionContext);
244-
rewrittenResponse.SetResultId(resultId, completionCapability);
239+
rewrittenResponse.SetResultId(resultId, _clientCapabilitiesService.ClientCapabilities);
245240

246241
return rewrittenResponse;
247242
}

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionListProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,11 @@ internal class RazorCompletionListProvider(
7878

7979
var completionList = CreateLSPCompletionList(razorCompletionItems, clientCapabilities);
8080

81-
var completionCapability = clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
82-
8381
// The completion list is cached and can be retrieved via this result id to enable the resolve completion functionality.
8482
var filePath = codeDocument.Source.FilePath.AssumeNotNull();
8583
var razorResolveContext = new RazorCompletionResolveContext(filePath, razorCompletionItems);
8684
var resultId = _completionListCache.Add(completionList, razorResolveContext);
87-
completionList.SetResultId(resultId, completionCapability);
85+
completionList.SetResultId(resultId, clientCapabilities);
8886

8987
return completionList;
9088
}

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionResolveData.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,22 @@ public static RazorCompletionResolveData Unwrap(CompletionItem completionItem)
3030
return context;
3131
}
3232

33-
public static void Wrap(VSInternalCompletionList completionList, TextDocumentIdentifier textDocument, bool supportsCompletionListData)
33+
public static void Wrap(VSInternalCompletionList completionList, TextDocumentIdentifier textDocument, VSInternalClientCapabilities clientCapabilities)
3434
{
3535
var data = new RazorCompletionResolveData(textDocument, OriginalData: null);
3636

37-
if (supportsCompletionListData)
37+
if (clientCapabilities.SupportsAnyCompletionListData())
3838
{
39-
if (completionList.Data is not null)
39+
if (clientCapabilities.SupportsCompletionListData() || completionList.Data is not null)
4040
{
4141
// Can set data at the completion list level
4242
completionList.Data = data with { OriginalData = completionList.Data };
4343
}
4444

45-
if (completionList.ItemDefaults?.Data is not null)
45+
if (clientCapabilities.SupportsCompletionListItemDefaultsData() || completionList.ItemDefaults?.Data is not null)
4646
{
4747
// Set data for the item defaults
48+
completionList.ItemDefaults ??= new();
4849
completionList.ItemDefaults.Data = data with { OriginalData = completionList.ItemDefaults.Data };
4950
}
5051

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionListExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ internal static class VSInternalCompletionListExtensions
1313
public static void SetResultId(
1414
this RazorVSInternalCompletionList completionList,
1515
int resultId,
16-
VSInternalCompletionSetting? completionSetting)
16+
VSInternalClientCapabilities clientCapabilities)
1717
{
1818
var data = JsonSerializer.SerializeToElement(new JsonObject()
1919
{
2020
[VSInternalCompletionItemExtensions.ResultIdKey] = resultId,
2121
});
2222

23-
if (completionSetting.SupportsCompletionListData())
23+
if (clientCapabilities.SupportsAnyCompletionListData())
2424
{
2525
// Ensure there is data at the completion list level, but only if ItemDefaults isn't set by the delegated server,
2626
// or if they've set both.

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionSettingExtensions.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/ClientCapabilitiesExtensions.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,20 @@ public static MarkupKind GetMarkupKind(this ClientCapabilities clientCapabilitie
2020
}
2121

2222
public static bool SupportsMarkdown(this ClientCapabilities clientCapabilities)
23-
{
24-
return clientCapabilities.GetMarkupKind() == MarkupKind.Markdown;
25-
}
23+
=> clientCapabilities.GetMarkupKind() == MarkupKind.Markdown;
2624

2725
public static bool SupportsVisualStudioExtensions(this ClientCapabilities clientCapabilities)
28-
{
29-
return (clientCapabilities as VSInternalClientCapabilities)?.SupportsVisualStudioExtensions ?? false;
30-
}
26+
=> clientCapabilities is VSInternalClientCapabilities { SupportsVisualStudioExtensions: true };
27+
28+
public static bool SupportsAnyCompletionListData(this ClientCapabilities clientCapabilities)
29+
=> clientCapabilities.SupportsCompletionListData() ||
30+
clientCapabilities.SupportsCompletionListItemDefaultsData();
31+
32+
public static bool SupportsCompletionListData(this ClientCapabilities clientCapabilities)
33+
=> clientCapabilities.SupportsVisualStudioExtensions() &&
34+
clientCapabilities.TextDocument?.Completion is VSInternalCompletionSetting { CompletionList.Data: true };
35+
36+
public static bool SupportsCompletionListItemDefaultsData(this ClientCapabilities clientCapabilities)
37+
=> clientCapabilities.TextDocument?.Completion?.CompletionListSetting?.ItemDefaults is { } defaults &&
38+
Array.IndexOf(defaults, "data") >= 0;
3139
}

src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,9 @@ private async ValueTask<Response> GetCompletionAsync(
234234
mappedPosition,
235235
razorCompletionOptions);
236236

237-
var completionCapability = clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
238-
239237
var resolutionContext = new DelegatedCompletionResolutionContext(identifier, RazorLanguageKind.CSharp, rewrittenResponse.Data ?? rewrittenResponse.ItemDefaults?.Data);
240238
var resultId = _completionListCache.Add(rewrittenResponse, resolutionContext);
241-
rewrittenResponse.SetResultId(resultId, completionCapability);
239+
rewrittenResponse.SetResultId(resultId, clientCapabilities);
242240

243241
return rewrittenResponse;
244242
}

src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionResolveEndpointTest.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public async Task Handle_EvictedCachedCompletionItem_NoChange()
7373
// Arrange
7474
var completionItem = new VSInternalCompletionItem() { Label = "Test" };
7575
var completionList = new RazorVSInternalCompletionList() { Items = [completionItem] };
76-
completionList.SetResultId(1337, completionSetting: null);
76+
completionList.SetResultId(1337, clientCapabilities: new());
7777
var parameters = ConvertToBridgedItem(completionItem);
7878
var requestContext = CreateRazorRequestContext(documentContext: null);
7979

@@ -91,7 +91,7 @@ public async Task Handle_CachedCompletionItem_Resolves()
9191
var completionItem = new VSInternalCompletionItem() { Label = "Test" };
9292
var completionList = new RazorVSInternalCompletionList() { Items = [completionItem] };
9393
var resultId = _completionListCache.Add(completionList, StrictMock.Of<ICompletionResolveContext>());
94-
completionList.SetResultId(resultId, completionSetting: null);
94+
completionList.SetResultId(resultId, clientCapabilities: new());
9595
var parameters = ConvertToBridgedItem(completionItem);
9696
var requestContext = CreateRazorRequestContext(documentContext: null);
9797

@@ -111,9 +111,9 @@ public async Task Handle_MultipleResultIdsIgnoresEvictedResultIds_Resolves()
111111
await InitializeAsync();
112112
var completionItem = new VSInternalCompletionItem() { Label = "Test" };
113113
var completionList = new RazorVSInternalCompletionList() { Items = [completionItem] };
114-
completionList.SetResultId(/* Invalid */ 1337, completionSetting: null);
114+
completionList.SetResultId(/* Invalid */ 1337, clientCapabilities: new());
115115
var resultId = _completionListCache.Add(completionList, StrictMock.Of<ICompletionResolveContext>());
116-
completionList.SetResultId(resultId, completionSetting: null);
116+
completionList.SetResultId(resultId, clientCapabilities: new());
117117
var parameters = ConvertToBridgedItem(completionItem);
118118
var requestContext = CreateRazorRequestContext(documentContext: null);
119119

@@ -131,17 +131,27 @@ public async Task Handle_MergedCompletionListFindsProperCompletionList_Resolves(
131131
{
132132
// Arrange
133133
await InitializeAsync();
134-
var completionSetting = new VSInternalCompletionSetting() { CompletionList = new VSInternalCompletionListSetting() { Data = true } };
134+
var clientCapabilities = new VSInternalClientCapabilities
135+
{
136+
SupportsVisualStudioExtensions = true,
137+
TextDocument = new()
138+
{
139+
Completion = new VSInternalCompletionSetting()
140+
{
141+
CompletionList = new VSInternalCompletionListSetting() { Data = true }
142+
}
143+
}
144+
};
135145
var completionList1 = new RazorVSInternalCompletionList() { Items = [] };
136146
var completion1Context = StrictMock.Of<ICompletionResolveContext>();
137147
var resultId1 = _completionListCache.Add(completionList1, completion1Context);
138-
completionList1.SetResultId(resultId1, completionSetting);
148+
completionList1.SetResultId(resultId1, clientCapabilities);
139149

140150
var completionItem = new VSInternalCompletionItem() { Label = "Test" };
141151
var completionList2 = new RazorVSInternalCompletionList() { Items = [completionItem] };
142152
var completion2Context = StrictMock.Of<ICompletionResolveContext>();
143153
var resultId2 = _completionListCache.Add(completionList2, completion2Context);
144-
completionList2.SetResultId(resultId2, completionSetting);
154+
completionList2.SetResultId(resultId2, clientCapabilities);
145155
var mergedCompletionList = CompletionListMerger.Merge(completionList1, completionList2);
146156
var mergedCompletionItem = mergedCompletionList.Items.Single();
147157
mergedCompletionItem.Data = mergedCompletionList.Data;
@@ -161,7 +171,7 @@ public async Task Handle_MergedCompletionListFindsProperCompletionList_Resolves(
161171
private VSInternalCompletionItem ConvertToBridgedItem(CompletionItem completionItem)
162172
{
163173
var list = new VSInternalCompletionList { Items = [completionItem] };
164-
RazorCompletionResolveData.Wrap(list, textDocument: new(), supportsCompletionListData: false);
174+
RazorCompletionResolveData.Wrap(list, textDocument: new(), clientCapabilities: new());
165175

166176
var serialized = JsonSerializer.Serialize(completionItem, SerializerOptions);
167177
var bridgedItem = JsonSerializer.Deserialize<VSInternalCompletionItem>(serialized, SerializerOptions);

0 commit comments

Comments
 (0)