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(); } }
public override async Task ProvideCompletionsAsync(CompletionContext completionContext) { var cancellationToken = completionContext.CancellationToken; var document = completionContext.Document; var workspace = document.Project.Solution.Workspace; var importCompletionOptionValue = completionContext.Options.GetOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, document.Project.Language); // Don't trigger import completion if the option value is "default" and the experiment is disabled for the user. if (importCompletionOptionValue == false || (importCompletionOptionValue == null && !IsTypeImportCompletionExperimentEnabled(workspace))) { return; } var syntaxContext = await CreateContextAsync(document, completionContext.Position, cancellationToken).ConfigureAwait(false); if (!syntaxContext.IsTypeContext) { return; } using (Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken)) using (var telemetryCounter = new TelemetryCounter()) { await AddCompletionItemsAsync(completionContext, syntaxContext, telemetryCounter, cancellationToken).ConfigureAwait(false); } }
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(); } }
public override async Task ProvideCompletionsAsync(CompletionContext completionContext) { try { var document = completionContext.Document; var position = completionContext.Position; var options = completionContext.Options; var cancellationToken = completionContext.CancellationToken; // If we were triggered by typing a character, then do a semantic check to make sure // we're still applicable. If not, then return immediately. if (completionContext.Trigger.Kind == CompletionTriggerKind.Insertion) { var isSemanticTriggerCharacter = await IsSemanticTriggerCharacterAsync(document, position - 1, cancellationToken).ConfigureAwait(false); if (!isSemanticTriggerCharacter) { return; } } completionContext.IsExclusive = IsExclusive(); using (Logger.LogBlock(FunctionId.Completion_SymbolCompletionProvider_GetItemsWorker, cancellationToken)) using (var telemetryCounter = new TelemetryCounter(ShouldCollectTelemetryForTargetTypeCompletion && IsTargetTypeCompletionFilterExperimentEnabled(document.Project.Solution.Workspace))) { var syntaxContext = await GetOrCreateContextAsync(document, position, cancellationToken).ConfigureAwait(false); var inferredTypes = new Lazy <ImmutableArray <ITypeSymbol> >(() => { var typeInferenceService = document.GetLanguageService <ITypeInferenceService>(); return(typeInferenceService.InferTypes(syntaxContext.SemanticModel, position, cancellationToken)); }); var regularItems = await GetItemsWorkerAsync(completionContext, syntaxContext, document, position, inferredTypes, options, preselect : false, telemetryCounter, cancellationToken).ConfigureAwait(false); completionContext.AddItems(regularItems); // We may or may not want to provide preselected items based on context. For example, C# avoids provide providing preselected items when // triggered via text insertion in an argument list. if (await ShouldProvidePreselectedItemsAsync(completionContext, syntaxContext, document, position, inferredTypes, options).ConfigureAwait(false)) { var preselectedItems = await GetItemsWorkerAsync(completionContext, syntaxContext, document, position, inferredTypes, options, preselect : true, telemetryCounter, cancellationToken).ConfigureAwait(false); completionContext.AddItems(preselectedItems); } } } catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e)) { // nop } }
/// <summary> /// Given a list of symbols, and a mapping from each symbol to its original SemanticModel, /// creates the list of completion items for them. /// </summary> private ImmutableArray <CompletionItem> CreateItems( CompletionContext completionContext, IEnumerable <ISymbol> symbols, Func <ISymbol, SyntaxContext> contextLookup, Dictionary <ISymbol, List <ProjectId> > invalidProjectMap, List <ProjectId> totalProjects, bool preselect, Lazy <ImmutableArray <ITypeSymbol> > inferredTypes, TelemetryCounter telemetryCounter) { // We might get symbol w/o name but CanBeReferencedByName is still set to true, // need to filter them out. // https://github.com/dotnet/roslyn/issues/47690 var symbolGroups = from symbol in symbols let texts = GetDisplayAndSuffixAndInsertionText(symbol, contextLookup(symbol)) where !string.IsNullOrWhiteSpace(texts.displayText) group symbol by texts into g select g; var itemListBuilder = ImmutableArray.CreateBuilder <CompletionItem>(); var typeConvertibilityCache = new Dictionary <ITypeSymbol, bool>(SymbolEqualityComparer.Default); foreach (var symbolGroup in symbolGroups) { var arbitraryFirstContext = contextLookup(symbolGroup.First()); var item = CreateItem( completionContext, symbolGroup.Key.displayText, symbolGroup.Key.suffix, symbolGroup.Key.insertionText, symbolGroup.ToList(), arbitraryFirstContext, invalidProjectMap, totalProjects, preselect); if (IsTargetTypeCompletionFilterExperimentEnabled(arbitraryFirstContext.Workspace)) { var tick = Environment.TickCount; foreach (var symbol in symbolGroup) { var syntaxContext = contextLookup(symbol); if (ShouldIncludeInTargetTypedCompletionList(symbol, inferredTypes.Value, syntaxContext.SemanticModel, syntaxContext.Position, typeConvertibilityCache)) { item = item.AddTag(WellKnownTags.TargetTypeMatch); break; } } telemetryCounter.AddTick(Environment.TickCount - tick); } itemListBuilder.Add(item); } return(itemListBuilder.ToImmutable()); }
/// <summary> /// Given a list of symbols, and a mapping from each symbol to its original SemanticModel, /// creates the list of completion items for them. /// </summary> private ImmutableArray <CompletionItem> CreateItems( IEnumerable <ISymbol> symbols, Func <ISymbol, SyntaxContext> contextLookup, Dictionary <ISymbol, List <ProjectId> > invalidProjectMap, List <ProjectId> totalProjects, bool preselect, Lazy <ImmutableArray <ITypeSymbol> > inferredTypes, TelemetryCounter telemetryCounter) { var symbolGroups = from symbol in symbols let texts = GetDisplayAndSuffixAndInsertionText(symbol, contextLookup(symbol)) group symbol by texts into g select g; var itemListBuilder = ImmutableArray.CreateBuilder <CompletionItem>(); var typeConvertibilityCache = new Dictionary <ITypeSymbol, bool>(); foreach (var symbolGroup in symbolGroups) { var arbitraryFirstContext = contextLookup(symbolGroup.First()); var item = this.CreateItem( symbolGroup.Key.displayText, symbolGroup.Key.suffix, symbolGroup.Key.insertionText, symbolGroup.ToList(), arbitraryFirstContext, invalidProjectMap, totalProjects, preselect); if (IsTargetTypeCompletionFilterExperimentEnabled(arbitraryFirstContext.Workspace)) { var tick = Environment.TickCount; var inferredTypesWithoutNullability = inferredTypes.Value.SelectAsArray(t => t.WithoutNullability()); foreach (var symbol in symbolGroup) { var syntaxContext = contextLookup(symbol); if (ShouldIncludeInTargetTypedCompletionList(symbol, inferredTypesWithoutNullability, syntaxContext.SemanticModel, syntaxContext.Position, typeConvertibilityCache)) { item = item.AddTag(WellKnownTags.TargetTypeMatch); break; } } telemetryCounter.AddTick(Environment.TickCount - tick); } itemListBuilder.Add(item); } return(itemListBuilder.ToImmutable()); }
public override async Task ProvideCompletionsAsync(CompletionContext completionContext) { var cancellationToken = completionContext.CancellationToken; var document = completionContext.Document; var workspace = document.Project.Solution.Workspace; // We need to check for context before option values, so we can tell completion service that we are in a context to provide expanded items // even though import completion might be disabled. This would show the expander in completion list which user can then use to explicitly ask for unimported items. var syntaxContext = await CreateContextAsync(document, completionContext.Position, cancellationToken).ConfigureAwait(false); if (!syntaxContext.IsTypeContext) { return; } completionContext.ExpandItemsAvailable = true; // We will trigger import completion regardless of the option/experiment if extended items is being requested explicitly (via expander in completion list) var isExpandedCompletion = completionContext.Options.GetOption(CompletionServiceOptions.IsExpandedCompletion); if (!isExpandedCompletion) { var importCompletionOptionValue = completionContext.Options.GetOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, document.Project.Language); // Don't trigger import completion if the option value is "default" and the experiment is disabled for the user. if (importCompletionOptionValue == false || (importCompletionOptionValue == null && !IsTypeImportCompletionExperimentEnabled(workspace))) { return; } } using (Logger.LogBlock(FunctionId.Completion_TypeImportCompletionProvider_GetCompletionItemsAsync, cancellationToken)) using (var telemetryCounter = new TelemetryCounter()) { await AddCompletionItemsAsync(completionContext, syntaxContext, isExpandedCompletion, telemetryCounter, cancellationToken).ConfigureAwait(false); } }
private async Task <IEnumerable <CompletionItem> > GetItemsWorkerAsync( CompletionContext completionContext, SyntaxContext syntaxContext, Document document, int position, Lazy <ImmutableArray <ITypeSymbol> > inferredTypes, OptionSet options, bool preselect, TelemetryCounter telemetryCounter, CancellationToken cancellationToken) { var relatedDocumentIds = GetRelatedDocumentIds(document, position); options = GetUpdatedRecommendationOptions(options, document.Project.Language); if (relatedDocumentIds.IsEmpty) { var itemsForCurrentDocument = await GetSymbolsAsync(position, preselect, syntaxContext, options, cancellationToken).ConfigureAwait(false); return(CreateItems(completionContext, itemsForCurrentDocument, _ => syntaxContext, invalidProjectMap: null, totalProjects: null, preselect, inferredTypes, telemetryCounter)); } var contextAndSymbolLists = await GetPerContextSymbolsAsync(document, position, options, new[] { document.Id }.Concat(relatedDocumentIds), preselect, cancellationToken).ConfigureAwait(false); var symbolToContextMap = UnionSymbols(contextAndSymbolLists); var missingSymbolsMap = FindSymbolsMissingInLinkedContexts(symbolToContextMap, contextAndSymbolLists); var totalProjects = contextAndSymbolLists.Select(t => t.documentId.ProjectId).ToList(); return(CreateItems(completionContext, symbolToContextMap.Keys, symbol => symbolToContextMap[symbol], missingSymbolsMap, totalProjects, preselect, inferredTypes, telemetryCounter)); }
private async Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, TelemetryCounter telemetryCounter, CancellationToken cancellationToken) { var document = completionContext.Document; var project = document.Project; var workspace = project.Solution.Workspace; var typeImportCompletionService = document.GetLanguageService <ITypeImportCompletionService>(); // Find all namespaces in scope at current cursor location, // which will be used to filter so the provider only returns out-of-scope types. var namespacesInScope = GetNamespacesInScope(document, syntaxContext, cancellationToken); // Get completion items from current project. var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); await typeImportCompletionService.GetTopLevelTypesAsync(project, syntaxContext, HandlePublicAndInternalItem, cancellationToken) .ConfigureAwait(false); // Get declarations from directly referenced projects and PEs var referencedAssemblySymbols = compilation.GetReferencedAssemblySymbols(); foreach (var assembly in referencedAssemblySymbols) { cancellationToken.ThrowIfCancellationRequested(); // Skip reference with only non-global alias. var metadataReference = compilation.GetMetadataReference(assembly); if (metadataReference.Properties.Aliases.IsEmpty || metadataReference.Properties.Aliases.Any(alias => alias == MetadataReferenceProperties.GlobalAlias)) { var assemblyProject = project.Solution.GetProject(assembly, cancellationToken); if (assemblyProject != null && assemblyProject.SupportsCompilation) { await typeImportCompletionService.GetTopLevelTypesAsync( assemblyProject, syntaxContext, GetHandler(compilation.Assembly, assembly), cancellationToken).ConfigureAwait(false); } else if (metadataReference is PortableExecutableReference peReference) { typeImportCompletionService.GetTopLevelTypesFromPEReference( project.Solution, compilation, peReference, syntaxContext, GetHandler(compilation.Assembly, assembly), cancellationToken); } } } telemetryCounter.ReferenceCount = referencedAssemblySymbols.Length; return; // Decide which item handler to use based on IVT Action <CompletionItem, bool> GetHandler(IAssemblySymbol assembly, IAssemblySymbol referencedAssembly) => assembly.IsSameAssemblyOrHasFriendAccessTo(referencedAssembly) ? (Action <CompletionItem, bool>)HandlePublicAndInternalItem : HandlePublicItem; // Add only public types to completion list void HandlePublicItem(CompletionItem item, bool isPublic) => AddItems(item, isPublic, isInternalsVisible: false, completionContext, namespacesInScope, telemetryCounter); // Add both public and internal types to completion list void HandlePublicAndInternalItem(CompletionItem item, bool isPublic) => AddItems(item, isPublic, isInternalsVisible: true, completionContext, namespacesInScope, telemetryCounter);
static void AddItems(CompletionItem item, bool isPublic, bool isInternalsVisible, CompletionContext completionContext, HashSet <string> namespacesInScope, TelemetryCounter counter) { if (isPublic || isInternalsVisible) { var containingNamespace = TypeImportCompletionItem.GetContainingNamespace(item); if (!namespacesInScope.Contains(containingNamespace)) { // We can return cached item directly, item's span will be fixed by completion service. // On the other hand, because of this (i.e. mutating the span of cached item for each run), // the provider can not be used as a service by components that might be run in parallel // with completion, which would be a race. completionContext.AddItem(item); counter.ItemsCount++;; } } }
private static void AddItems(ImmutableArray <CompletionItem> items, CompletionContext completionContext, HashSet <string> namespacesInScope, TelemetryCounter counter) { counter.ReferenceCount++; foreach (var item in items) { var containingNamespace = ImportCompletionItem.GetContainingNamespace(item); if (!namespacesInScope.Contains(containingNamespace)) { // We can return cached item directly, item's span will be fixed by completion service. // On the other hand, because of this (i.e. mutating the span of cached item for each run), // the provider can not be used as a service by components that might be run in parallel // with completion, which would be a race. completionContext.AddItem(item); counter.ItemsCount++; } } }
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); }