提交 93b312f8 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #15309 from CyrusNajmabadi/parallelCompletion

Call into all CompletionProviders in parallel to speed up completion.
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -212,7 +213,7 @@ internal protected CompletionProvider GetProvider(CompletionItem item) ...@@ -212,7 +213,7 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var defaultItemSpan = this.GetDefaultCompletionListSpan(text, caretPosition); var defaultItemSpan = this.GetDefaultCompletionListSpan(text, caretPosition);
options = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);; options = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var providers = GetFilteredProviders(roles, trigger, options); var providers = GetFilteredProviders(roles, trigger, options);
var completionProviderToIndex = GetCompletionProviderToIndex(providers); var completionProviderToIndex = GetCompletionProviderToIndex(providers);
...@@ -220,38 +221,43 @@ internal protected CompletionProvider GetProvider(CompletionItem item) ...@@ -220,38 +221,43 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var triggeredProviders = ImmutableArray<CompletionProvider>.Empty; var triggeredProviders = ImmutableArray<CompletionProvider>.Empty;
switch (trigger.Kind) switch (trigger.Kind)
{ {
case CompletionTriggerKind.Insertion: case CompletionTriggerKind.Insertion:
case CompletionTriggerKind.Deletion: case CompletionTriggerKind.Deletion:
if (this.ShouldTriggerCompletion(text, caretPosition, trigger, roles, options)) if (this.ShouldTriggerCompletion(text, caretPosition, trigger, roles, options))
{
triggeredProviders = providers.Where(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options)).ToImmutableArrayOrEmpty();
if (triggeredProviders.Length == 0)
{ {
triggeredProviders = providers; triggeredProviders = providers.Where(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options)).ToImmutableArrayOrEmpty();
if (triggeredProviders.Length == 0)
{
triggeredProviders = providers;
}
} }
} break;
break; default:
default: triggeredProviders = providers;
triggeredProviders = providers; break;
break;
} }
// Now, ask all the triggered providers if they can provide a group. // Now, ask all the triggered providers, in parallel, to populate a completion context.
var completionContexts = new List<CompletionContext>(); // Note: we keep any context with items *or* with a suggested item.
foreach (var provider in triggeredProviders) var triggeredCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
document, caretPosition, trigger, options,
defaultItemSpan, triggeredProviders,
cancellationToken).ConfigureAwait(false);
// If we didn't even get any back with items, then there's nothing to do.
// i.e. if only got items back that had only suggestion items, then we don't
// want to show any completion.
if (!triggeredCompletionContexts.Any(cc => cc.Items.Count > 0))
{ {
var completionContext = await GetContextAsync( return null;
provider, document, caretPosition, trigger,
options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
if (completionContext != null)
{
completionContexts.Add(completionContext);
}
} }
// See if there was a group provided that was exclusive and had items in it. If so, then // All the contexts should be non-empty or have a suggestion item.
Debug.Assert(triggeredCompletionContexts.All(HasAnyItems));
// See if there was a completion context provided that was exclusive. If so, then
// that's all we'll return. // that's all we'll return.
var firstExclusiveContext = completionContexts.FirstOrDefault(t => t.IsExclusive && t.Items.Any()); var firstExclusiveContext = triggeredCompletionContexts.FirstOrDefault(t => t.IsExclusive);
if (firstExclusiveContext != null) if (firstExclusiveContext != null)
{ {
...@@ -261,41 +267,51 @@ internal protected CompletionProvider GetProvider(CompletionItem item) ...@@ -261,41 +267,51 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
isExclusive: true); isExclusive: true);
} }
// If no exclusive providers provided anything, then go through the remaining // Shouldn't be any exclusive completion contexts at this point.
// triggered list and see if any provide items. Debug.Assert(triggeredCompletionContexts.All(cc => !cc.IsExclusive));
var nonExclusiveLists = completionContexts.Where(t => !t.IsExclusive).ToList();
// If we still don't have any items, then we're definitely done. // Great! We had some items. Now we want to see if any of the other providers
if (!nonExclusiveLists.Any(g => g.Items.Any())) // would like to augment the completion list. For example, we might trigger
{ // enum-completion on space. If enum completion results in any items, then
return null; // we'll want to augment the list with all the regular symbol completion items.
} var augmentingProviders = providers.Except(triggeredProviders).ToImmutableArray();
// If we do have items, then ask all the other (non exclusive providers) if they var augmentingCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
// want to augment the items. document, caretPosition, trigger, options, defaultItemSpan,
var usedProviders = nonExclusiveLists.Select(g => g.Provider); augmentingProviders, cancellationToken).ConfigureAwait(false);
var nonUsedProviders = providers.Except(usedProviders);
var nonUsedNonExclusiveLists = new List<CompletionContext>();
foreach (var provider in nonUsedProviders)
{
var completionList = await GetContextAsync(provider, document, caretPosition, trigger, options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
if (completionList != null && !completionList.IsExclusive)
{
nonUsedNonExclusiveLists.Add(completionList);
}
}
var allProvidersAndLists = nonExclusiveLists.Concat(nonUsedNonExclusiveLists).ToList(); var allContexts = triggeredCompletionContexts.Concat(augmentingCompletionContexts);
if (allProvidersAndLists.Count == 0) Debug.Assert(allContexts.Length > 0);
{
return null;
}
// Providers are ordered, but we processed them in our own order. Ensure that the // Providers are ordered, but we processed them in our own order. Ensure that the
// groups are properly ordered based on the original providers. // groups are properly ordered based on the original providers.
allProvidersAndLists.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]); allContexts = allContexts.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);
return MergeAndPruneCompletionLists(allContexts, defaultItemSpan, isExclusive: false);
}
private static bool HasAnyItems(CompletionContext cc)
{
return cc.Items.Count > 0 || cc.SuggestionModeItem != null;
}
private async Task<ImmutableArray<CompletionContext>> ComputeNonEmptyCompletionContextsAsync(
Document document, int caretPosition, CompletionTrigger trigger,
OptionSet options, TextSpan defaultItemSpan,
ImmutableArray<CompletionProvider> providers,
CancellationToken cancellationToken)
{
var completionContextTasks = new List<Task<CompletionContext>>();
foreach (var provider in providers)
{
completionContextTasks.Add(GetContextAsync(
provider, document, caretPosition, trigger,
options, defaultItemSpan, cancellationToken));
}
return MergeAndPruneCompletionLists(allProvidersAndLists, defaultItemSpan, isExclusive: false); var completionContexts = await Task.WhenAll(completionContextTasks).ConfigureAwait(false);
var nonEmptyContexts = completionContexts.Where(HasAnyItems).ToImmutableArray();
return nonEmptyContexts;
} }
private CompletionList MergeAndPruneCompletionLists( private CompletionList MergeAndPruneCompletionLists(
...@@ -520,4 +536,4 @@ int IEqualityComparer<ImmutableHashSet<string>>.GetHashCode(ImmutableHashSet<str ...@@ -520,4 +536,4 @@ int IEqualityComparer<ImmutableHashSet<string>>.GetHashCode(ImmutableHashSet<str
return hash; return hash;
} }
} }
} }
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册