Skip to content

Commit 5f0b5a3

Browse files
Refactor CompletionTriggerAndCommitCharacters
This change transforms CompletionTriggerAndCommitCharacters from being a partly static and partly instance service to being fully instance-based. Here is a summary of the changes: - All collections are now private and operations on them are exposed via instance methods. - I've switched from FrozenSet<string> to HashSet<char>. Honestly, I doubt we were getting much value from FrozenSet<string> for strings with the same length. - All initialization is performed in the constructor. - Accessing AllTriggerCharacters or AllCommitCharacters returns a new array each time to avoid modifying the original array. - The Razor transition character ('@') is now a constant rather than a set with a single element.
1 parent 1dbebdd commit 5f0b5a3

File tree

4 files changed

+82
-41
lines changed

4 files changed

+82
-41
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ internal class RazorCompletionEndpoint(
2525
: IRazorRequestHandler<CompletionParams, VSInternalCompletionList?>, ICapabilitiesProvider
2626
{
2727
private readonly CompletionListProvider _completionListProvider = completionListProvider;
28-
private readonly CompletionTriggerAndCommitCharacters _completionTriggerAndCommitCharacters = completionTriggerAndCommitCharacters;
28+
private readonly CompletionTriggerAndCommitCharacters _triggerAndCommitCharacters = completionTriggerAndCommitCharacters;
2929
private readonly ITelemetryReporter? _telemetryReporter = telemetryReporter;
3030
private readonly RazorLSPOptionsMonitor _optionsMonitor = optionsMonitor;
3131

@@ -40,8 +40,8 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
4040
serverCapabilities.CompletionProvider = new CompletionOptions()
4141
{
4242
ResolveProvider = true,
43-
TriggerCharacters = _completionTriggerAndCommitCharacters.AllTriggerCharacters,
44-
AllCommitCharacters = CompletionTriggerAndCommitCharacters.AllCommitCharacters
43+
TriggerCharacters = _triggerAndCommitCharacters.AllTriggerCharacters,
44+
AllCommitCharacters = _triggerAndCommitCharacters.AllCommitCharacters
4545
};
4646
}
4747

Lines changed: 77 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,117 @@
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.Collections.Frozen;
5-
using System.Linq;
4+
using System.Collections.Generic;
5+
using System.Collections.Immutable;
66
using Microsoft.CodeAnalysis.Razor.Workspaces;
77
using Microsoft.VisualStudio.LanguageServer.Protocol;
88

99
namespace Microsoft.CodeAnalysis.Razor.Completion;
1010

11-
internal class CompletionTriggerAndCommitCharacters(LanguageServerFeatureOptions languageServerFeatureOptions)
11+
internal class CompletionTriggerAndCommitCharacters
1212
{
1313
/// <summary>
1414
/// Trigger character that can trigger both Razor and Delegation completion
1515
/// </summary>
16-
private const string RazorDelegationTriggerCharacter = "@";
16+
private const char TransitionCharacter = '@';
1717

18-
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
18+
private static readonly char[] s_vsHtmlTriggerCharacters = [':', '#', '.', '!', '*', ',', '(', '[', '-', '<', '&', '\\', '/', '\'', '"', '=', ':', ' ', '`'];
19+
private static readonly char[] s_vsCodeHtmlTriggerCharacters = ['#', '.', '!', ',', '-', '<'];
20+
private static readonly char[] s_razorTriggerCharacters = ['<', ':', ' '];
21+
private static readonly char[] s_csharpTriggerCharacters = [' ', '(', '=', '#', '.', '<', '[', '{', '"', '/', ':', '~'];
22+
private static readonly char[] s_commitCharacters = [' ', '>', ';', '='];
1923

20-
private static readonly FrozenSet<string> s_vsHtmlTriggerCharacters = new[] { RazorDelegationTriggerCharacter, ":", "#", ".", "!", "*", ",", "(", "[", "-", "<", "&", "\\", "/", "'", "\"", "=", ":", " ", "`" }.ToFrozenSet();
21-
private static readonly FrozenSet<string> s_vsCodeHtmlTriggerCharacters = new[] { RazorDelegationTriggerCharacter, "#", ".", "!", ",", "-", "<", }.ToFrozenSet();
22-
private FrozenSet<string>? _allDelegationTriggerCharacters;
23-
private string[]? _allTriggerCharacters;
24+
private readonly HashSet<char> _csharpTriggerCharacters;
25+
private readonly HashSet<char> _delegationTriggerCharacters;
26+
private readonly HashSet<char> _htmlTriggerCharacters;
27+
private readonly HashSet<char> _razorTriggerCharacters;
28+
private readonly ImmutableArray<string> _allTriggerCharacters;
29+
private readonly ImmutableArray<string> _allCommitCharacters;
2430

25-
private static readonly FrozenSet<string> s_razorTriggerCharacters = new[] { RazorDelegationTriggerCharacter, "<", ":", " " }.ToFrozenSet();
26-
private static readonly FrozenSet<string> s_razorDelegationTriggerCharacters = new[] { RazorDelegationTriggerCharacter }.ToFrozenSet();
27-
private static readonly FrozenSet<string> s_csharpTriggerCharacters = new[] { " ", "(", "=", "#", ".", "<", "[", "{", "\"", "/", ":", "~" }.ToFrozenSet();
28-
29-
private FrozenSet<string> HtmlTriggerCharacters =>
30-
_languageServerFeatureOptions.UseVsCodeCompletionTriggerCharacters ? s_vsCodeHtmlTriggerCharacters : s_vsHtmlTriggerCharacters;
31-
32-
private FrozenSet<string> AllDelegationTriggerCharacters => _allDelegationTriggerCharacters
33-
??= s_razorDelegationTriggerCharacters.Union(s_csharpTriggerCharacters).Union(HtmlTriggerCharacters).ToFrozenSet();
34-
35-
public string[] AllTriggerCharacters => _allTriggerCharacters ??= [.. s_razorTriggerCharacters.Union(AllDelegationTriggerCharacters)];
31+
// Note: Create a new array each time to avoid modifying the original array.
32+
public string[] AllTriggerCharacters => [.. _allTriggerCharacters];
3633

3734
/// <summary>
3835
/// This is the intersection of C# and HTML commit characters.
3936
/// </summary>
4037
// We need to specify it so that platform can correctly calculate ApplicableToSpan in
4138
// https://devdiv.visualstudio.com/DevDiv/_git/VSLanguageServerClient?path=/src/product/RemoteLanguage/Impl/Features/Completion/AsyncCompletionSource.cs&version=GBdevelop&line=855&lineEnd=855&lineStartColumn=9&lineEndColumn=49&lineStyle=plain&_a=contents
4239
// This is needed to fix https://github.com/dotnet/razor/issues/10787 in particular
43-
public static string[] AllCommitCharacters = [" ", ">", ";", "="];
44-
45-
public static bool IsValidTrigger(FrozenSet<string> triggerCharacters, CompletionContext completionContext)
46-
=> completionContext.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
47-
completionContext.TriggerCharacter is null ||
48-
triggerCharacters.Contains(completionContext.TriggerCharacter);
40+
// Note: Create a new array each time to avoid modifying the original array.
41+
public string[] AllCommitCharacters => [.. _allCommitCharacters];
42+
43+
public CompletionTriggerAndCommitCharacters(LanguageServerFeatureOptions languageServerFeatureOptions)
44+
{
45+
// C# trigger characters (do NOT include '@')
46+
var csharpTriggerCharacters = new HashSet<char>();
47+
csharpTriggerCharacters.UnionWith(s_csharpTriggerCharacters);
48+
49+
// HTML trigger characters (include '@' + HTML trigger characters)
50+
var htmlTriggerCharacters = new HashSet<char>() { TransitionCharacter };
51+
52+
if (languageServerFeatureOptions.UseVsCodeCompletionTriggerCharacters)
53+
{
54+
htmlTriggerCharacters.UnionWith(s_vsCodeHtmlTriggerCharacters);
55+
}
56+
else
57+
{
58+
htmlTriggerCharacters.UnionWith(s_vsHtmlTriggerCharacters);
59+
}
60+
61+
// Delegation trigger characters (include '@' + C# and HTML trigger characters)
62+
var delegationTriggerCharacters = new HashSet<char> { TransitionCharacter };
63+
delegationTriggerCharacters.UnionWith(csharpTriggerCharacters);
64+
delegationTriggerCharacters.UnionWith(htmlTriggerCharacters);
65+
66+
// Razor trigger characters (include '@' + Razor trigger characters)
67+
var razorTriggerCharacters = new HashSet<char>() { TransitionCharacter };
68+
razorTriggerCharacters.UnionWith(s_razorTriggerCharacters);
69+
70+
// All trigger characters (include Razor + Delegation trigger characters)
71+
var allTriggerCharacters = new HashSet<char>();
72+
allTriggerCharacters.UnionWith(razorTriggerCharacters);
73+
allTriggerCharacters.UnionWith(delegationTriggerCharacters);
74+
75+
var commitCharacters = new HashSet<char>();
76+
commitCharacters.UnionWith(s_commitCharacters);
77+
78+
_csharpTriggerCharacters = csharpTriggerCharacters;
79+
_htmlTriggerCharacters = htmlTriggerCharacters;
80+
_razorTriggerCharacters = razorTriggerCharacters;
81+
_delegationTriggerCharacters = delegationTriggerCharacters;
82+
_allTriggerCharacters = allTriggerCharacters.SelectAsArray(static c => c.ToString());
83+
_allCommitCharacters = commitCharacters.SelectAsArray(static c => c.ToString());
84+
}
4985

5086
public bool IsValidCSharpTrigger(CompletionContext completionContext)
51-
=> IsValidTrigger(s_csharpTriggerCharacters, completionContext);
87+
=> IsValidTrigger(completionContext, _csharpTriggerCharacters);
5288

5389
public bool IsValidDelegationTrigger(CompletionContext completionContext)
54-
=> IsValidTrigger(AllDelegationTriggerCharacters, completionContext);
90+
=> IsValidTrigger(completionContext, _delegationTriggerCharacters);
5591

5692
public bool IsValidHtmlTrigger(CompletionContext completionContext)
57-
=> IsValidTrigger(HtmlTriggerCharacters, completionContext);
93+
=> IsValidTrigger(completionContext, _htmlTriggerCharacters);
5894

5995
public bool IsValidRazorTrigger(CompletionContext completionContext)
60-
=> IsValidTrigger(s_razorTriggerCharacters, completionContext);
96+
=> IsValidTrigger(completionContext, _razorTriggerCharacters);
97+
98+
private static bool IsValidTrigger(CompletionContext completionContext, HashSet<char> triggerCharacters)
99+
=> completionContext.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
100+
completionContext.TriggerCharacter is not [var c] ||
101+
triggerCharacters.Contains(c);
61102

62103
public bool IsCSharpTriggerCharacter(string ch)
63-
=> s_csharpTriggerCharacters.Contains(ch);
104+
=> ch is [var c] && _csharpTriggerCharacters.Contains(c);
64105

65106
public bool IsDelegationTriggerCharacter(string ch)
66-
=> AllDelegationTriggerCharacters.Contains(ch);
107+
=> ch is [var c] && _delegationTriggerCharacters.Contains(c);
67108

68109
public bool IsHtmlTriggerCharacter(string ch)
69-
=> HtmlTriggerCharacters.Contains(ch);
110+
=> ch is [var c] && _htmlTriggerCharacters.Contains(c);
70111

71112
public bool IsRazorTriggerCharacter(string ch)
72-
=> s_razorTriggerCharacters.Contains(ch);
113+
=> ch is [var c] && _razorTriggerCharacters.Contains(c);
73114

74-
public bool IsRazorDelegationTriggerCharacter(string ch)
75-
=> ch == RazorDelegationTriggerCharacter;
115+
public bool IsTransitionCharacter(string ch)
116+
=> ch is [TransitionCharacter];
76117
}

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/DelegatedCompletionHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ internal static class DelegatedCompletionHelper
8080
};
8181

8282
if (languageKind == RazorLanguageKind.CSharp
83-
&& triggerAndCommitCharacters.IsRazorDelegationTriggerCharacter(triggerCharacter))
83+
&& triggerAndCommitCharacters.IsTransitionCharacter(triggerCharacter))
8484
{
8585
// The C# language server will not return any completions for the '@' character unless we
8686
// send the completion request explicitly.

src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostDocumentCompletionEndpoint.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
6969
{
7070
ResolveProvider = false, // TODO - change to true when Resolve is implemented
7171
TriggerCharacters = _triggerAndCommitCharacters.AllTriggerCharacters,
72-
AllCommitCharacters = CompletionTriggerAndCommitCharacters.AllCommitCharacters
72+
AllCommitCharacters = _triggerAndCommitCharacters.AllCommitCharacters
7373
}
7474
}];
7575
}

0 commit comments

Comments
 (0)