protected override async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, HashSet <string> namespacesInScope, bool isExpandedCompletion, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken)) { var telemetryCounter = new TelemetryCounter(); var typeImportCompletionService = completionContext.Document.GetRequiredLanguageService <ITypeImportCompletionService>(); var itemsFromAllAssemblies = await typeImportCompletionService.GetAllTopLevelTypesAsync( completionContext.Document.Project, syntaxContext, forceCacheCreation : isExpandedCompletion, cancellationToken).ConfigureAwait(false); if (itemsFromAllAssemblies == null) { telemetryCounter.CacheMiss = true; } else { foreach (var items in itemsFromAllAssemblies) { AddItems(items, completionContext, namespacesInScope, telemetryCounter); } } telemetryCounter.Report(); } }
protected override async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, HashSet <string> namespacesInScope, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken)) { var telemetryCounter = new TelemetryCounter(); var typeImportCompletionService = completionContext.Document.GetRequiredLanguageService <ITypeImportCompletionService>(); var(itemsFromAllAssemblies, isPartialResult) = await typeImportCompletionService.GetAllTopLevelTypesAsync( completionContext.Document.Project, syntaxContext, forceCacheCreation : completionContext.CompletionOptions.ForceExpandedCompletionIndexCreation, completionContext.CompletionOptions, cancellationToken).ConfigureAwait(false); var aliasTargetNamespaceToTypeNameMap = GetAliasTypeDictionary(completionContext.Document, syntaxContext, cancellationToken); foreach (var items in itemsFromAllAssemblies) { AddItems(items, completionContext, namespacesInScope, aliasTargetNamespaceToTypeNameMap, telemetryCounter); } if (isPartialResult) { telemetryCounter.CacheMiss = true; } telemetryCounter.Report(); } }
protected override async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, HashSet <string> namespacesInScope, bool isExpandedCompletion, CancellationToken cancellationToken) { using var _ = Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken); var telemetryCounter = new TelemetryCounter(); var document = completionContext.Document; var project = document.Project; var workspace = project.Solution.Workspace; var typeImportCompletionService = document.GetLanguageService <ITypeImportCompletionService>() !; var tasksToGetCompletionItems = ArrayBuilder <Task <ImmutableArray <CompletionItem> > > .GetInstance(); // Get completion items from current project. var compilation = (await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false)) !; tasksToGetCompletionItems.Add(Task.Run(() => typeImportCompletionService.GetTopLevelTypesAsync( project, syntaxContext, isInternalsVisible: true, cancellationToken))); // Get declarations from directly referenced projects and PEs. // For script compilation, we don't want previous submissions returned as referenced assemblies, // there's no need to check for unimported type from them since namespace declaration is not allowed in script. var referencedAssemblySymbols = compilation.GetReferencedAssemblySymbols(excludePreviousSubmissions: true); // This can be parallelized because we don't add items to CompletionContext // until all the collected tasks are completed. tasksToGetCompletionItems.AddRange( referencedAssemblySymbols.Select(symbol => Task.Run(() => HandleReferenceAsync(symbol)))); // We want to timebox the operation that might need to traverse all the type symbols and populate the cache. // The idea is not to block completion for too long (likely to happen the first time import completion is triggered). // The trade-off is we might not provide unimported types until the cache is warmed up. var timeoutInMilliseconds = completionContext.Options.GetOption(CompletionServiceOptions.TimeoutInMillisecondsForImportCompletion); var combinedTask = Task.WhenAll(tasksToGetCompletionItems.ToImmutableAndFree()); if (isExpandedCompletion || timeoutInMilliseconds != 0 && await Task.WhenAny(combinedTask, Task.Delay(timeoutInMilliseconds, cancellationToken)).ConfigureAwait(false) == combinedTask) { // Either there's no timeout, and we now have all completion items ready, // or user asked for unimported type explicitly so we need to wait until they are calculated. var completionItemsToAdd = await combinedTask.ConfigureAwait(false); foreach (var completionItems in completionItemsToAdd) { AddItems(completionItems, completionContext, namespacesInScope, telemetryCounter); } } else { // If timed out, we don't want to cancel the computation so next time the cache would be populated. // We do not keep track if previous compuation for a given project/PE reference is still running. So there's a chance // we queue same computation again later. However, we expect such computation for an individual reference to be relatively // fast so the actual cycles wasted would be insignificant. telemetryCounter.TimedOut = true; } telemetryCounter.ReferenceCount = referencedAssemblySymbols.Length; telemetryCounter.Report(); return; async Task <ImmutableArray <CompletionItem> > HandleReferenceAsync(IAssemblySymbol referencedAssemblySymbol) { cancellationToken.ThrowIfCancellationRequested(); // Skip reference with only non-global alias. var metadataReference = compilation.GetMetadataReference(referencedAssemblySymbol); if (metadataReference.Properties.Aliases.IsEmpty || metadataReference.Properties.Aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias)) { var assemblyProject = project.Solution.GetProject(referencedAssemblySymbol, cancellationToken); if (assemblyProject != null && assemblyProject.SupportsCompilation) { return(await typeImportCompletionService.GetTopLevelTypesAsync( assemblyProject, syntaxContext, isInternalsVisible : compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(referencedAssemblySymbol), cancellationToken).ConfigureAwait(false)); } else if (metadataReference is PortableExecutableReference peReference) { return(typeImportCompletionService.GetTopLevelTypesFromPEReference( project.Solution, compilation, peReference, syntaxContext, isInternalsVisible: compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(referencedAssemblySymbol), cancellationToken)); } } return(ImmutableArray <CompletionItem> .Empty); }