Skip to content

Commit 27d647e

Browse files
committed
抽象CompletionSourceBase、CompletionSourceProviderBase
1 parent a32dcf7 commit 27d647e

File tree

6 files changed

+347
-237
lines changed

6 files changed

+347
-237
lines changed

src/ChinesePinyinIntelliSenseExtender.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
</Compile>
7171
<Compile Include="Options\OptionPages.cs" />
7272
<Compile Include="Options\Model\GeneralOptions.cs" />
73+
<Compile Include="Completion\CompletionSourceBase.cs" />
74+
<Compile Include="Completion\CompletionSourceProviderBase.cs" />
7375
<Compile Include="Ref\InputMethodDictionary\InputMethodDictionaryUtilities.cs" />
7476
<Compile Include="Ref\InputMethodDictionary\InputMethodReverseDictionary.cs" />
7577
<Compile Include="Ref\InputMethodDictionary\ITextAdjuster.cs" />
@@ -98,8 +100,8 @@
98100
<Compile Include="Usings.cs" />
99101
<Compile Include="Util\StringPreMatchUtil.cs" />
100102
<Compile Include="Util\InputMethodDictionaryLoader.cs" />
101-
<Compile Include="PinyinAsyncCompletionSource.cs" />
102-
<Compile Include="PinyinAsyncCompletionSourceProvider.cs" />
103+
<Compile Include="Completion\Async\PinyinAsyncCompletionSource.cs" />
104+
<Compile Include="Completion\Async\PinyinAsyncCompletionSourceProvider.cs" />
103105
<Compile Include="Properties\AssemblyInfo.cs" />
104106
<Compile Include="Util\ChineseCheckUtil.cs" />
105107
<Compile Include="ObjectBoolean.cs" />

src/PinyinAsyncCompletionSource.cs renamed to src/Completion/Async/PinyinAsyncCompletionSource.cs

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,27 @@
1313
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
1414
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
1515
using Microsoft.VisualStudio.Text;
16-
using Microsoft.VisualStudio.Utilities;
1716

18-
namespace ChinesePinyinIntelliSenseExtender;
17+
namespace ChinesePinyinIntelliSenseExtender.Completion.Async;
1918

20-
internal class PinyinAsyncCompletionSource : IAsyncCompletionSource
19+
internal class PinyinAsyncCompletionSource : CompletionSourceBase, IAsyncCompletionSource
2120
{
2221
#region Private 字段
2322

2423
private static readonly CompletionFilter s_chineseFilter = new("中文代码", "C", new(KnownMonikers.Attribute.ToImageId(), "中文代码"));
2524

2625
private static readonly ImmutableArray<CompletionFilter> s_chineseFilters = ImmutableArray.Create(s_chineseFilter);
2726

28-
/// <summary>
29-
/// <see cref="GetCompletionContextAsync(IAsyncCompletionSession, CompletionTrigger, SnapshotPoint, SnapshotSpan, CancellationToken)"/> 递归标记
30-
/// </summary>
31-
private static readonly AsyncLocal<bool> s_getCompletionContextRecursionTag = new();
32-
33-
private readonly GeneralOptions _options;
3427
private readonly IEnumerable<IAsyncCompletionSource> _otherAsyncCompletionSources;
35-
private InputMethodDictionaryGroup? _inputMethodDictionaryGroup;
3628

3729
#endregion Private 字段
3830

3931
#region Public 构造函数
4032

4133
public PinyinAsyncCompletionSource(IEnumerable<IAsyncCompletionSource> otherAsyncCompletionSources, GeneralOptions options)
34+
: base(options)
4235
{
4336
_otherAsyncCompletionSources = otherAsyncCompletionSources ?? throw new ArgumentNullException(nameof(otherAsyncCompletionSources));
44-
_options = options ?? throw new ArgumentNullException(nameof(options));
4537
}
4638

4739
#endregion Public 构造函数
@@ -50,32 +42,34 @@ public PinyinAsyncCompletionSource(IEnumerable<IAsyncCompletionSource> otherAsyn
5042

5143
public async Task<CompletionContext?> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token)
5244
{
53-
if (s_getCompletionContextRecursionTag.Value
54-
|| !_options.Enable)
45+
if (CanNotProcess())
5546
{
5647
return null;
5748
}
5849

5950
try
6051
{
61-
s_getCompletionContextRecursionTag.Value = true;
52+
s_completionContextRecursionTag.Value = true;
6253

63-
if (_inputMethodDictionaryGroup is null
64-
|| _inputMethodDictionaryGroup.IsDisposed)
65-
{
66-
_inputMethodDictionaryGroup = await InputMethodDictionaryGroupProvider.GetAsync();
67-
}
54+
var getInputMethodDictionaryGroupTask = GetInputMethodDictionaryGroupAsync();
6855

6956
var tasks = _otherAsyncCompletionSources.Select(m => m.GetCompletionContextAsync(session, trigger, triggerLocation, applicableToSpan, token)).ToArray();
7057

7158
await Task.WhenAll(tasks);
7259

7360
token.ThrowIfCancellationRequested();
7461

75-
Func<string, bool> shouldProcessCheckDelegate = StringPreMatchUtil.GetPreCheckPredicate(_options.PreMatchType, _options.PreCheckRule);
62+
Func<string, bool> shouldProcessCheckDelegate = StringPreMatchUtil.GetPreCheckPredicate(Options.PreMatchType, Options.PreCheckRule);
7663

7764
var allCompletionItems = tasks.SelectMany(static m => m.Status == TaskStatus.RanToCompletion && m.Result?.Items is not null ? m.Result.Items.AsEnumerable() : Array.Empty<CompletionItem>());
7865

66+
if (!allCompletionItems.Any())
67+
{
68+
return null;
69+
}
70+
71+
var inputMethodDictionaryGroup = await getInputMethodDictionaryGroupTask;
72+
7973
var count = tasks.Sum(static m => m.Status == TaskStatus.RanToCompletion && m.Result?.Items is not null ? m.Result.Items.Length : 0);
8074

8175
var itemBuffer = ArrayPool<CompletionItem>.Shared.Rent(count * 5); //预留足够多的空间,避免字典过多导致的问题
@@ -84,7 +78,7 @@ public PinyinAsyncCompletionSource(IEnumerable<IAsyncCompletionSource> otherAsyn
8478
int bufferIndex = 0;
8579
allCompletionItems.AsParallel()
8680
.WithCancellation(token)
87-
.ForAll(m => CreateCompletionItemWithConvertion(m, shouldProcessCheckDelegate, itemBuffer, ref bufferIndex));
81+
.ForAll(m => CreateCompletionItemWithConvertion(m, inputMethodDictionaryGroup, shouldProcessCheckDelegate, itemBuffer, ref bufferIndex));
8882

8983
if (bufferIndex > 0)
9084
{
@@ -99,7 +93,7 @@ public PinyinAsyncCompletionSource(IEnumerable<IAsyncCompletionSource> otherAsyn
9993
}
10094
finally
10195
{
102-
s_getCompletionContextRecursionTag.Value = false;
96+
s_completionContextRecursionTag.Value = false;
10397
}
10498
}
10599

@@ -125,7 +119,7 @@ public CompletionStartData InitializeCompletion(CompletionTrigger trigger, Snaps
125119

126120
#region impl
127121

128-
private void CreateCompletionItemWithConvertion(CompletionItem originCompletionItem, Func<string, bool> shouldProcessCheck, CompletionItem[] itemBuffer, ref int bufferIndex)
122+
private void CreateCompletionItemWithConvertion(CompletionItem originCompletionItem, InputMethodDictionaryGroup inputMethodDictionaryGroup, Func<string, bool> shouldProcessCheck, CompletionItem[] itemBuffer, ref int bufferIndex)
129123
{
130124
var originInsertText = originCompletionItem.InsertText;
131125

@@ -134,28 +128,28 @@ private void CreateCompletionItemWithConvertion(CompletionItem originCompletionI
134128
return;
135129
}
136130

137-
var spellings = _inputMethodDictionaryGroup!.FindAll(originInsertText);
131+
var spellings = inputMethodDictionaryGroup.FindAll(originInsertText);
138132

139133
if (spellings.Length == 0)
140134
{
141135
return;
142136
}
143137

144-
if (_options.EnableFSharpSupport
138+
if (Options.EnableFSharpSupport
145139
&& originCompletionItem.Properties.TryGetProperty("RoslynCompletionItemData", out object data)
146140
&& TryGetRoslynItemNameInCode(data, out var nameInCode))
147141
{
148142
originInsertText = nameInCode!;
149143
}
150144

151-
if (_options.SingleWordsDisplay)
145+
if (Options.SingleWordsDisplay)
152146
{
153147
foreach (var spelling in spellings)
154148
{
155149
itemBuffer[Interlocked.Increment(ref bufferIndex) - 1] = CreateCompletionItem(originCompletionItem, originInsertText, spelling);
156150
}
157151
}
158-
else if (_options.EnableMultipleSpellings)
152+
else if (Options.EnableMultipleSpellings)
159153
{
160154
itemBuffer[Interlocked.Increment(ref bufferIndex) - 1] = CreateCompletionItem(originCompletionItem, originInsertText, string.Join("/", spellings));
161155
}
@@ -169,11 +163,11 @@ private void CreateCompletionItemWithConvertion(CompletionItem originCompletionI
169163

170164
private CompletionItem CreateCompletionItem(CompletionItem originCompletionItem, string originInsertText, string spelling)
171165
{
172-
var newCompletionItem = new CompletionItem(displayText: Format(_options.DisplayTextFormat, originCompletionItem.DisplayText, spelling),
166+
var newCompletionItem = new CompletionItem(displayText: FormatString(Options.DisplayTextFormat, originCompletionItem.DisplayText, spelling),
173167
source: this,
174168
icon: originCompletionItem.Icon,
175169
filters: s_chineseFilters,
176-
suffix: Format(_options.DisplaySuffixFormat, originCompletionItem.Suffix, spelling),
170+
suffix: FormatString(Options.DisplaySuffixFormat, originCompletionItem.Suffix, spelling),
177171
insertText: originInsertText,
178172
sortText: spelling,
179173
filterText: spelling,
@@ -187,23 +181,6 @@ private CompletionItem CreateCompletionItem(CompletionItem originCompletionItem,
187181
newCompletionItem.Properties.AddProperty(this, originCompletionItem);
188182

189183
return newCompletionItem;
190-
191-
static string Format(string? format, string origin, string spellings)
192-
{
193-
if (string.IsNullOrEmpty(format))
194-
{
195-
return origin;
196-
}
197-
var builder = PooledStringBuilder.GetInstance();
198-
try
199-
{
200-
return builder.Builder.AppendFormat(format, origin, spellings).ToString();
201-
}
202-
finally
203-
{
204-
builder.Free();
205-
}
206-
}
207184
}
208185

209186
#endregion CreateCompletionItem
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#nullable enable
2+
3+
using System.ComponentModel.Composition;
4+
using System.Diagnostics;
5+
using System.Threading;
6+
7+
using ChinesePinyinIntelliSenseExtender.Options;
8+
9+
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
10+
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data;
11+
using Microsoft.VisualStudio.Text;
12+
using Microsoft.VisualStudio.Text.Editor;
13+
using Microsoft.VisualStudio.Utilities;
14+
15+
namespace ChinesePinyinIntelliSenseExtender.Completion.Async;
16+
17+
[Export(typeof(IAsyncCompletionSourceProvider))]
18+
[Name("中文代码拼音补全")]
19+
[ContentType("text")]
20+
internal class PinyinAsyncCompletionSourceProvider : CompletionSourceProviderBase<ITextView, IAsyncCompletionSource>, IAsyncCompletionSourceProvider
21+
{
22+
#region Private 字段
23+
24+
[ImportMany]
25+
private readonly Lazy<IAsyncCompletionSourceProvider>[] _lazyAsyncCompletionSourceProviders = null!;
26+
27+
#endregion Private 字段
28+
29+
#region Public 方法
30+
31+
protected override IAsyncCompletionSource CreateCompletionSource(ITextView dependence)
32+
{
33+
var currentContentType = dependence.TextBuffer.ContentType;
34+
35+
var otherAsyncCompletionSources = _lazyAsyncCompletionSourceProviders
36+
.Where(m => CheckShouldCreateCompletionSource(m.Value, currentContentType))
37+
.Select(lazy =>
38+
{
39+
try
40+
{
41+
return lazy.Value.GetOrCreate(dependence);
42+
}
43+
catch { }
44+
return null;
45+
})
46+
.Where(m => m is not null)
47+
.ToList()!;
48+
49+
Debug.WriteLine($"Total {otherAsyncCompletionSources?.Count ?? 0} IAsyncCompletionSource found.");
50+
51+
IAsyncCompletionSource completionSource = otherAsyncCompletionSources is null || otherAsyncCompletionSources.Count == 0
52+
? EmptyAsyncCompletionSource.Instance
53+
: new PinyinAsyncCompletionSource(otherAsyncCompletionSources!, Options);
54+
55+
return completionSource;
56+
}
57+
58+
protected override string? GetCurrentEditFilePath(ITextView dependence)
59+
{
60+
if (dependence.TextBuffer.Properties.TryGetProperty<ITextDocument>(typeof(ITextDocument), out var textDocument))
61+
{
62+
return textDocument?.FilePath;
63+
}
64+
return null;
65+
}
66+
67+
protected override IAsyncCompletionSource GetDefaultCompletionSource(ITextView dependence)
68+
{
69+
return EmptyAsyncCompletionSource.Instance;
70+
}
71+
72+
#endregion Public 方法
73+
74+
#region Private 类
75+
76+
private class EmptyAsyncCompletionSource : IAsyncCompletionSource
77+
{
78+
#region Public 属性
79+
80+
public static EmptyAsyncCompletionSource Instance { get; } = new();
81+
82+
#endregion Public 属性
83+
84+
#region Public 方法
85+
86+
public Task<CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token) => Task.FromResult<CompletionContext>(null!);
87+
88+
public Task<object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token) => Task.FromResult<object>(null!);
89+
90+
public CompletionStartData InitializeCompletion(CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token) => CompletionStartData.DoesNotParticipateInCompletion;
91+
92+
#endregion Public 方法
93+
}
94+
95+
#endregion Private 类
96+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#nullable enable
2+
3+
using System.Threading;
4+
5+
using ChinesePinyinIntelliSenseExtender.Options;
6+
using ChinesePinyinIntelliSenseExtender.Util;
7+
using Microsoft.VisualStudio.Utilities;
8+
9+
namespace ChinesePinyinIntelliSenseExtender.Completion;
10+
11+
internal abstract class CompletionSourceBase
12+
{
13+
#region Protected 字段
14+
15+
/// <summary>
16+
/// 递归标记
17+
/// </summary>
18+
protected static readonly AsyncLocal<bool> s_completionContextRecursionTag = new();
19+
20+
protected readonly GeneralOptions Options;
21+
22+
#endregion Protected 字段
23+
24+
#region Private 字段
25+
26+
private InputMethodDictionaryGroup? _inputMethodDictionaryGroup;
27+
28+
#endregion Private 字段
29+
30+
#region Public 构造函数
31+
32+
public CompletionSourceBase(GeneralOptions options)
33+
{
34+
Options = options ?? throw new ArgumentNullException(nameof(options));
35+
}
36+
37+
#endregion Public 构造函数
38+
39+
#region Protected 方法
40+
41+
protected static string FormatString(string? format, string origin, string spellings)
42+
{
43+
if (string.IsNullOrEmpty(format))
44+
{
45+
return origin;
46+
}
47+
var builder = PooledStringBuilder.GetInstance();
48+
try
49+
{
50+
return builder.Builder.AppendFormat(format, origin, spellings).ToString();
51+
}
52+
finally
53+
{
54+
builder.Free();
55+
}
56+
}
57+
58+
protected bool CanNotProcess()
59+
{
60+
return s_completionContextRecursionTag.Value
61+
|| !Options.Enable;
62+
}
63+
64+
protected InputMethodDictionaryGroup GetInputMethodDictionaryGroup()
65+
{
66+
if (_inputMethodDictionaryGroup is null
67+
|| _inputMethodDictionaryGroup.IsDisposed)
68+
{
69+
_inputMethodDictionaryGroup = InputMethodDictionaryGroupProvider.GetAsync().ConfigureAwait(false).GetAwaiter().GetResult();
70+
}
71+
return _inputMethodDictionaryGroup;
72+
}
73+
74+
protected async Task<InputMethodDictionaryGroup> GetInputMethodDictionaryGroupAsync()
75+
{
76+
if (_inputMethodDictionaryGroup is null
77+
|| _inputMethodDictionaryGroup.IsDisposed)
78+
{
79+
_inputMethodDictionaryGroup = await InputMethodDictionaryGroupProvider.GetAsync();
80+
}
81+
return _inputMethodDictionaryGroup;
82+
}
83+
84+
#endregion Protected 方法
85+
}

0 commit comments

Comments
 (0)