Skip to content

Commit 284b32d

Browse files
authored
Minor clean up of some completion code (#11609)
Starting to look at cohosting completion resolve and noticed some things that annoyed me :)
2 parents 0bfbdab + a19a75d commit 284b32d

File tree

14 files changed

+141
-134
lines changed

14 files changed

+141
-134
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ internal class DelegatedCompletionItemResolver(
3131
public override async Task<VSInternalCompletionItem?> ResolveAsync(
3232
VSInternalCompletionItem item,
3333
VSInternalCompletionList containingCompletionList,
34-
object? originalRequestContext,
34+
ICompletionResolveContext originalRequestContext,
3535
VSInternalClientCapabilities? clientCapabilities,
3636
IComponentAvailabilityService componentAvailabilityService,
3737
CancellationToken cancellationToken)

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

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT license. See License.txt in the project root for license information.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Linq;
56
using System.Threading;
67
using System.Threading.Tasks;
@@ -33,37 +34,8 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
3334

3435
public async Task<VSInternalCompletionItem> HandleRequestAsync(VSInternalCompletionItem completionItem, RazorRequestContext requestContext, CancellationToken cancellationToken)
3536
{
36-
if (!completionItem.TryGetCompletionListResultIds(out var resultIds))
37-
{
38-
// Unable to lookup completion item result info
39-
return completionItem;
40-
}
41-
42-
object? originalRequestContext = null;
43-
VSInternalCompletionList? containingCompletionList = null;
44-
foreach (var resultId in resultIds)
45-
{
46-
if (!_completionListCache.TryGet(resultId, out var cacheEntry))
47-
{
48-
continue;
49-
}
50-
51-
// See if this is the right completion list for this corresponding completion item. We cross-check this based on label only given that
52-
// is what users interact with.
53-
if (cacheEntry.CompletionList.Items.Any(completion =>
54-
completionItem.Label == completion.Label
55-
// Check the Kind as well, e.g. we may have a Razor snippet and a C# keyword with the same label, etc.
56-
&& completionItem.Kind == completion.Kind))
57-
{
58-
originalRequestContext = cacheEntry.Context;
59-
containingCompletionList = cacheEntry.CompletionList;
60-
break;
61-
}
62-
}
63-
64-
if (containingCompletionList is null)
37+
if (!TryGetOriginalRequestData(completionItem, out var containingCompletionList, out var originalRequestContext))
6538
{
66-
// Couldn't find an associated completion list
6739
return completionItem;
6840
}
6941

@@ -81,4 +53,33 @@ public async Task<VSInternalCompletionItem> HandleRequestAsync(VSInternalComplet
8153

8254
return resolvedCompletionItem;
8355
}
56+
57+
private bool TryGetOriginalRequestData(VSInternalCompletionItem completionItem, [NotNullWhen(true)] out VSInternalCompletionList? completionList, [NotNullWhen(true)] out ICompletionResolveContext? context)
58+
{
59+
context = null;
60+
completionList = null;
61+
62+
if (!completionItem.TryGetCompletionListResultIds(out var resultIds))
63+
{
64+
// Unable to lookup completion item result info
65+
return false;
66+
}
67+
68+
foreach (var resultId in resultIds)
69+
{
70+
// See if this is the right completion list for this corresponding completion item. We cross-check this based on label only given that
71+
// is what users interact with.
72+
if (_completionListCache.TryGet(resultId, out completionList, out context) &&
73+
completionList.Items.Any(
74+
completion =>
75+
completionItem.Label == completion.Label &&
76+
// Check the Kind as well, e.g. we may have a Razor snippet and a C# keyword with the same label, etc.
77+
completionItem.Kind == completion.Kind))
78+
{
79+
return true;
80+
}
81+
}
82+
83+
return false;
84+
}
8485
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public AggregateCompletionItemResolver(IEnumerable<CompletionItemResolver> compl
2828
public async Task<VSInternalCompletionItem?> ResolveAsync(
2929
VSInternalCompletionItem item,
3030
VSInternalCompletionList containingCompletionList,
31-
object? originalRequestContext,
31+
ICompletionResolveContext originalRequestContext,
3232
VSInternalClientCapabilities? clientCapabilities,
3333
IComponentAvailabilityService componentAvailabilityService,
3434
CancellationToken cancellationToken)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ internal abstract class CompletionItemResolver
1313
public abstract Task<VSInternalCompletionItem?> ResolveAsync(
1414
VSInternalCompletionItem item,
1515
VSInternalCompletionList containingCompletionList,
16-
object? originalRequestContext,
16+
ICompletionResolveContext originalRequestContext,
1717
VSInternalClientCapabilities? clientCapabilities,
1818
IComponentAvailabilityService componentAvailabilityService,
1919
CancellationToken cancellationToken);

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

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT license. See License.txt in the project root for license information.
33

4-
using System;
4+
using System.Diagnostics.CodeAnalysis;
55
using Microsoft.VisualStudio.LanguageServer.Protocol;
66

77
namespace Microsoft.CodeAnalysis.Razor.Completion;
88

99
internal class CompletionListCache
1010
{
11-
private record struct Slot(
12-
int Id,
13-
VSInternalCompletionList CompletionList,
14-
object? Context,
15-
bool Used = true);
11+
private record struct Slot(int Id, VSInternalCompletionList CompletionList, ICompletionResolveContext Context);
1612

1713
// Internal for testing
1814
internal const int MaxCacheSize = 10;
@@ -25,13 +21,8 @@ private record struct Slot(
2521
private int _nextIndex;
2622
private int _nextId;
2723

28-
public int Add(VSInternalCompletionList completionList, object? context)
24+
public int Add(VSInternalCompletionList completionList, ICompletionResolveContext context)
2925
{
30-
if (completionList is null)
31-
{
32-
throw new ArgumentNullException(nameof(completionList));
33-
}
34-
3526
lock (_accessLock)
3627
{
3728
var index = _nextIndex++;
@@ -52,7 +43,7 @@ public int Add(VSInternalCompletionList completionList, object? context)
5243
}
5344
}
5445

55-
public bool TryGet(int id, out (VSInternalCompletionList CompletionList, object? Context) result)
46+
public bool TryGet(int id, [NotNullWhen(true)] out VSInternalCompletionList? completionList, [NotNullWhen(true)] out ICompletionResolveContext? context)
5647
{
5748
lock (_accessLock)
5849
{
@@ -74,22 +65,27 @@ public bool TryGet(int id, out (VSInternalCompletionList CompletionList, object?
7465

7566
var slot = _items[index];
7667

77-
if (!slot.Used)
68+
// CompletionList is annotated as non-nullable, but we are allocating an array of 10 items for our cache, so initially
69+
// those array entries will be default. By checking for null here, we detect if we're hitting an unused part of the array
70+
// so stop looping.
71+
if (slot.CompletionList is null)
7872
{
7973
break;
8074
}
8175

8276
if (slot.Id == id)
8377
{
84-
result = (slot.CompletionList, slot.Context);
78+
completionList = slot.CompletionList;
79+
context = slot.Context;
8580
return true;
8681
}
8782

8883
count--;
8984
}
9085

9186
// A cache entry associated with the given id was not found.
92-
result = default;
87+
completionList = null;
88+
context = null;
9389
return false;
9490
}
9591
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Collections.Immutable;
66
using System.Diagnostics;
7+
using System.Diagnostics.CodeAnalysis;
78
using System.Linq;
89
using System.Text.Json;
910
using Microsoft.AspNetCore.Razor.PooledObjects;
@@ -17,6 +18,8 @@ internal static class CompletionListMerger
1718
private static readonly string s_data2Key = nameof(MergedCompletionListData.Data2);
1819
private static readonly object s_emptyData = new();
1920

21+
[return: NotNullIfNotNull(nameof(razorCompletionList))]
22+
[return: NotNullIfNotNull(nameof(delegatedCompletionList))]
2023
public static VSInternalCompletionList? Merge(VSInternalCompletionList? razorCompletionList, VSInternalCompletionList? delegatedCompletionList)
2124
{
2225
if (razorCompletionList is null)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT license. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.CodeAnalysis.Razor.Completion;
5+
6+
/// <summary>
7+
/// Marker interface to make the CompletionListCache API easier to use
8+
/// </summary>
9+
internal interface ICompletionResolveContext
10+
{
11+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ internal class RazorCompletionItemResolver : CompletionItemResolver
1717
public override async Task<VSInternalCompletionItem?> ResolveAsync(
1818
VSInternalCompletionItem completionItem,
1919
VSInternalCompletionList containingCompletionList,
20-
object? originalRequestContext,
20+
ICompletionResolveContext originalRequestContext,
2121
VSInternalClientCapabilities? clientCapabilities,
2222
IComponentAvailabilityService componentAvailabilityService,
2323
CancellationToken cancellationToken)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55

66
namespace Microsoft.CodeAnalysis.Razor.Completion;
77

8-
internal record RazorCompletionResolveContext(string FilePath, ImmutableArray<RazorCompletionItem> CompletionItems);
8+
internal record RazorCompletionResolveContext(string FilePath, ImmutableArray<RazorCompletionItem> CompletionItems) : ICompletionResolveContext;

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/DelegatedTypes.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Microsoft.CodeAnalysis.Razor.Protocol;
1212

13+
using Microsoft.CodeAnalysis.Razor.Completion;
1314
using Microsoft.VisualStudio.LanguageServer.Protocol;
1415

1516
internal record DelegatedSpellCheckParams(
@@ -70,7 +71,7 @@ internal record DelegatedMapCodeParams(
7071

7172
internal record DelegatedCompletionResolutionContext(
7273
[property: JsonPropertyName("originalRequestParams")] DelegatedCompletionParams OriginalRequestParams,
73-
[property: JsonPropertyName("originalCompletionListData")] object? OriginalCompletionListData);
74+
[property: JsonPropertyName("originalCompletionListData")] object? OriginalCompletionListData) : ICompletionResolveContext;
7475

7576
internal record DelegatedCompletionItemResolveParams(
7677
[property: JsonPropertyName("identifier")] TextDocumentIdentifierAndVersion Identifier,

0 commit comments

Comments
 (0)