|
1 | 1 | // Copyright (c) .NET Foundation. All rights reserved. |
2 | 2 | // Licensed under the MIT license. See License.txt in the project root for license information. |
3 | 3 |
|
4 | | -using System.Collections.Frozen; |
5 | | -using System.Linq; |
| 4 | +using System.Collections.Generic; |
| 5 | +using System.Collections.Immutable; |
6 | 6 | using Microsoft.CodeAnalysis.Razor.Workspaces; |
7 | 7 | using Microsoft.VisualStudio.LanguageServer.Protocol; |
8 | 8 |
|
9 | 9 | namespace Microsoft.CodeAnalysis.Razor.Completion; |
10 | 10 |
|
11 | | -internal class CompletionTriggerAndCommitCharacters(LanguageServerFeatureOptions languageServerFeatureOptions) |
| 11 | +internal class CompletionTriggerAndCommitCharacters |
12 | 12 | { |
13 | 13 | /// <summary> |
14 | 14 | /// Trigger character that can trigger both Razor and Delegation completion |
15 | 15 | /// </summary> |
16 | | - private const string RazorDelegationTriggerCharacter = "@"; |
| 16 | + private const char TransitionCharacter = '@'; |
17 | 17 |
|
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 = [' ', '>', ';', '=']; |
19 | 23 |
|
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; |
24 | 30 |
|
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]; |
36 | 33 |
|
37 | 34 | /// <summary> |
38 | 35 | /// This is the intersection of C# and HTML commit characters. |
39 | 36 | /// </summary> |
40 | 37 | // We need to specify it so that platform can correctly calculate ApplicableToSpan in |
41 | 38 | // 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 |
42 | 39 | // 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 | + } |
49 | 85 |
|
50 | 86 | public bool IsValidCSharpTrigger(CompletionContext completionContext) |
51 | | - => IsValidTrigger(s_csharpTriggerCharacters, completionContext); |
| 87 | + => IsValidTrigger(completionContext, _csharpTriggerCharacters); |
52 | 88 |
|
53 | 89 | public bool IsValidDelegationTrigger(CompletionContext completionContext) |
54 | | - => IsValidTrigger(AllDelegationTriggerCharacters, completionContext); |
| 90 | + => IsValidTrigger(completionContext, _delegationTriggerCharacters); |
55 | 91 |
|
56 | 92 | public bool IsValidHtmlTrigger(CompletionContext completionContext) |
57 | | - => IsValidTrigger(HtmlTriggerCharacters, completionContext); |
| 93 | + => IsValidTrigger(completionContext, _htmlTriggerCharacters); |
58 | 94 |
|
59 | 95 | 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); |
61 | 102 |
|
62 | 103 | public bool IsCSharpTriggerCharacter(string ch) |
63 | | - => s_csharpTriggerCharacters.Contains(ch); |
| 104 | + => ch is [var c] && _csharpTriggerCharacters.Contains(c); |
64 | 105 |
|
65 | 106 | public bool IsDelegationTriggerCharacter(string ch) |
66 | | - => AllDelegationTriggerCharacters.Contains(ch); |
| 107 | + => ch is [var c] && _delegationTriggerCharacters.Contains(c); |
67 | 108 |
|
68 | 109 | public bool IsHtmlTriggerCharacter(string ch) |
69 | | - => HtmlTriggerCharacters.Contains(ch); |
| 110 | + => ch is [var c] && _htmlTriggerCharacters.Contains(c); |
70 | 111 |
|
71 | 112 | public bool IsRazorTriggerCharacter(string ch) |
72 | | - => s_razorTriggerCharacters.Contains(ch); |
| 113 | + => ch is [var c] && _razorTriggerCharacters.Contains(c); |
73 | 114 |
|
74 | | - public bool IsRazorDelegationTriggerCharacter(string ch) |
75 | | - => ch == RazorDelegationTriggerCharacter; |
| 115 | + public bool IsTransitionCharacter(string ch) |
| 116 | + => ch is [TransitionCharacter]; |
76 | 117 | } |
0 commit comments