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);
                }
        }
Exemple #3
0
        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();
            }
        }
Exemple #4
0
        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());
        }
Exemple #7
0
        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);
                }
        }
Exemple #8
0
        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);
            }