Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support ItemDefaults.Data on completion lists
  • Loading branch information
davidwengier committed Jun 11, 2025
commit 8c4bd782123459857ec9781d8456c58e631f0004
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ internal class DelegatedCompletionListProvider(
: DelegatedCompletionHelper.RewriteHtmlResponse(delegatedResponse, razorCompletionOptions);

var completionCapability = clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
var resolutionContext = new DelegatedCompletionResolutionContext(identifier, positionInfo.LanguageKind, rewrittenResponse.Data);
var resolutionContext = new DelegatedCompletionResolutionContext(identifier, positionInfo.LanguageKind, rewrittenResponse.Data ?? rewrittenResponse.ItemDefaults?.Data);
var resultId = _completionListCache.Add(rewrittenResponse, resolutionContext);
rewrittenResponse.SetResultId(resultId, completionCapability);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
var completionCapability = _clientCapabilitiesService.ClientCapabilities.TextDocument?.Completion as VSInternalCompletionSetting;

var razorDocumentIdentifier = new TextDocumentIdentifierAndVersion(request.TextDocument, Version: 0);
var resolutionContext = new DelegatedCompletionResolutionContext(razorDocumentIdentifier, RazorLanguageKind.Html, rewrittenResponse.Data);
var resolutionContext = new DelegatedCompletionResolutionContext(razorDocumentIdentifier, RazorLanguageKind.Html, rewrittenResponse.Data ?? rewrittenResponse.ItemDefaults?.Data);
var resultId = _completionListCache.Add(rewrittenResponse, resolutionContext);
rewrittenResponse.SetResultId(resultId, completionCapability);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ internal static class CompletionListMerger
// We don't fully support merging continue characters currently. Razor doesn't currently use them so delegated completion lists always win.
var mergedContinueWithCharacters = razorCompletionList.ContinueCharacters ?? delegatedCompletionList.ContinueCharacters;

var mergedItemDefaultsData = MergeData(razorCompletionList.ItemDefaults?.Data, delegatedCompletionList.ItemDefaults?.Data);
// We don't fully support merging edit ranges currently. Razor doesn't currently use them so delegated completion lists always win.
var mergedItemDefaultsEditRange = razorCompletionList.ItemDefaults?.EditRange ?? delegatedCompletionList.ItemDefaults?.EditRange;

Expand All @@ -58,6 +59,7 @@ internal static class CompletionListMerger
ContinueCharacters = mergedContinueWithCharacters,
ItemDefaults = new CompletionListItemDefaults()
{
Data = mergedItemDefaultsData,
EditRange = mergedItemDefaultsEditRange,
}
};
Expand Down Expand Up @@ -152,14 +154,16 @@ private static void TrySplitJsonElement(object data, ref PooledArrayBuilder<Json

private static void EnsureMergeableData(RazorVSInternalCompletionList completionListA, RazorVSInternalCompletionList completionListB)
{
if (completionListA.Data != completionListB.Data &&
(completionListA.Data is null || completionListB.Data is null))
var completionListAData = completionListA.Data ?? completionListA.ItemDefaults?.Data;
var completionListBData = completionListB.Data ?? completionListB.ItemDefaults?.Data;
if (completionListAData != completionListBData &&
(completionListAData is null || completionListBData is null))
{
// One of the completion lists have data while the other does not, we need to ensure that any non-data centric items don't get incorrect data associated

// The candidate completion list will be one where we populate empty data for any `null` specifying data given we'll be merging
// two completion lists together we don't want incorrect data to be inherited down
var candidateCompletionList = completionListA.Data is null ? completionListA : completionListB;
var candidateCompletionList = completionListAData is null ? completionListA : completionListB;
for (var i = 0; i < candidateCompletionList.Items.Length; i++)
{
var item = candidateCompletionList.Items[i];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,17 @@ public static void Wrap(VSInternalCompletionList completionList, TextDocumentIde

if (supportsCompletionListData)
{
// Can set data at the completion list level
completionList.Data = data with { OriginalData = completionList.Data };
if (completionList.Data is not null)
{
// Can set data at the completion list level
completionList.Data = data with { OriginalData = completionList.Data };
}

if (completionList.ItemDefaults?.Data is not null)
{
// Set data for the item defaults
completionList.ItemDefaults.Data = data with { OriginalData = completionList.ItemDefaults.Data };
}

// Set data for items that won't inherit the default
foreach (var completionItem in completionList.Items.Where(static c => c.Data is not null))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.AspNetCore.Razor;

namespace Microsoft.CodeAnalysis.Razor.Completion;

Expand All @@ -24,26 +25,31 @@ public static void SetResultId(

if (completionSetting.SupportsCompletionListData())
{
// Can set data at the completion list level
// Ensure there is data at the completion list level, but only if ItemDefaults isn't set by the delegated server,
// or if they've set both.
var hasDefaultsData = completionList.ItemDefaults?.Data is not null;
if (completionList.Data is not null || !hasDefaultsData)
{
completionList.Data = CompletionListMerger.MergeData(data, completionList.Data);
}

var mergedData = CompletionListMerger.MergeData(data, completionList.Data);
completionList.Data = mergedData;
if (hasDefaultsData)
{
completionList.ItemDefaults.AssumeNotNull().Data = CompletionListMerger.MergeData(data, completionList.ItemDefaults.Data);
}

// Merge data for items that won't inherit the default
foreach (var completionItem in completionList.Items.Where(c => c.Data is not null))
{
mergedData = CompletionListMerger.MergeData(data, completionItem.Data);
completionItem.Data = mergedData;
completionItem.Data = CompletionListMerger.MergeData(data, completionItem.Data);
}
}
else
{
// No CompletionList.Data support

foreach (var completionItem in completionList.Items)
{
var mergedData = CompletionListMerger.MergeData(data, completionItem.Data);
completionItem.Data = mergedData;
completionItem.Data = CompletionListMerger.MergeData(data, completionItem.Data);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ private async ValueTask<Response> GetCompletionAsync(

var completionCapability = clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;

var resolutionContext = new DelegatedCompletionResolutionContext(identifier, RazorLanguageKind.CSharp, rewrittenResponse.Data);
var resolutionContext = new DelegatedCompletionResolutionContext(identifier, RazorLanguageKind.CSharp, rewrittenResponse.Data ?? rewrittenResponse.ItemDefaults?.Data);
var resultId = _completionListCache.Add(rewrittenResponse, resolutionContext);
rewrittenResponse.SetResultId(resultId, completionCapability);

Expand Down