in src/Editor/Language/Impl/Language/AsyncCompletion/AsyncCompletionBroker.cs [334:437]
private void GetCompletionSources(
SnapshotPoint triggerLocation,
Func<IContentType, ITextViewRoleSet, IReadOnlyList<Lazy<IAsyncCompletionSourceProvider, IOrderableContentTypeAndOptionalTextViewRoleMetadata>>> getImports,
ITextSnapshot rootSnapshot,
ITextView textView,
IBufferGraph bufferGraph,
CompletionTrigger trigger,
CompletionSessionTelemetry telemetry,
CancellationToken token,
out List<(IAsyncCompletionSource Source, SnapshotPoint Point)> sourcesWithLocations,
out SnapshotSpan applicableToSpan)
{
var sourcesWithData = MetadataUtilities<IAsyncCompletionSourceProvider, IOrderableContentTypeAndOptionalTextViewRoleMetadata>
.GetBuffersAndImports(triggerLocation, rootSnapshot, textView.Roles, getImports);
var applicableToSpanBuilder = default(SnapshotSpan);
bool applicableToSpanExists = false;
bool anySourceParticipates = false;
bool anySourceExclusive = false;
var sourcesWithLocationsBuilder = new List<(IAsyncCompletionSource, SnapshotPoint, CompletionParticipation)>();
foreach (var (buffer, point, import) in sourcesWithData)
{
telemetry.UiStopwatch.Restart();
var sourceProvider = GuardedOperations.InstantiateExtension(this, import);
var source = GuardedOperations.CallExtensionPoint(
errorSource: sourceProvider,
call: () => sourceProvider.GetOrCreate(textView),
valueOnThrow: null);
if (source == null)
{
telemetry.UiStopwatch.Stop();
telemetry.RecordObtainingSourceSpan(source, telemetry.UiStopwatch.ElapsedMilliseconds);
continue;
}
// Iterate through all sources and add them to collection
var startData = GuardedOperations.CallExtensionPoint(
errorSource: source,
call: () => source.InitializeCompletion(trigger, point, token),
valueOnThrow: CompletionStartData.DoesNotParticipateInCompletion);
telemetry.UiStopwatch.Stop();
telemetry.RecordObtainingSourceSpan(source, telemetry.UiStopwatch.ElapsedMilliseconds);
if (!applicableToSpanExists && startData.ApplicableToSpan != default)
{
applicableToSpanExists = true;
applicableToSpanBuilder = startData.ApplicableToSpan;
}
if (startData.Participation == CompletionParticipation.ProvidesItems)
{
anySourceParticipates = true;
}
else if (startData.Participation == CompletionParticipation.ExclusivelyProvidesItems)
{
anySourceParticipates = true;
anySourceExclusive = true;
}
sourcesWithLocationsBuilder.Add((source, point, startData.Participation));
}
// Map the applicable to span to the view's text snapshot and use it for completion,
if (applicableToSpanExists)
{
if (rootSnapshot == textView.TextSnapshot)
{
// Typical case; ApplicableToSpan is always defined on TextView.TextBuffer, so we will map up
var mappingSpan = bufferGraph.CreateMappingSpan(applicableToSpanBuilder, SpanTrackingMode.EdgeInclusive);
var spans = mappingSpan.GetSpans(textView.TextSnapshot);
if (spans.Count == 0)
throw new InvalidOperationException("Completion expects the Applicable To Span to be mappable to the view's TextBuffer.");
applicableToSpanBuilder = spans[0];
}
else
{
// Edge case; in Roslyn's DebuggerTextView, TextView.TextSnapshot is below the root snapshot
// ApplicableToSpan is always defined on textView's TextBuffer, so we will to map down
var spans = MappingHelper.MapDownToBufferNoTrack(applicableToSpanBuilder, textView.TextBuffer);
if (spans.Count == 0)
throw new InvalidOperationException("Completion expects the Applicable To Span to be mappable to the view's TextBuffer.");
applicableToSpanBuilder = spans[0];
}
}
// Copying temporary values because we can't access out&ref params in lambdas
if (anySourceExclusive)
{
sourcesWithLocations = sourcesWithLocationsBuilder.Where(n => n.Item3 == CompletionParticipation.ExclusivelyProvidesItems).Select(n => (n.Item1, n.Item2)).ToList();
}
else if (anySourceParticipates)
{
sourcesWithLocations = sourcesWithLocationsBuilder.Where(n => n.Item3 == CompletionParticipation.ProvidesItems).Select(n => (n.Item1, n.Item2)).ToList();
}
else
{
sourcesWithLocations = new List<(IAsyncCompletionSource Source, SnapshotPoint Point)>();
}
applicableToSpan = applicableToSpanBuilder;
}