Exemple #1
0
 internal override Task <(CompletionList?completionList, bool expandItemsAvailable)> GetCompletionsInternalAsync(
     Document document,
     int caretPosition,
     CompletionOptions options,
     CompletionTrigger trigger,
     ImmutableHashSet <string>?roles,
     CancellationToken cancellationToken)
 {
     return(GetCompletionsWithAvailabilityOfExpandedItemsAsync(document, caretPosition, trigger, roles, options, cancellationToken));
 }
Exemple #2
0
 /// <summary>
 /// Gets the completions available at the caret position.
 /// </summary>
 /// <param name="document">The document that completion is occurring within.</param>
 /// <param name="caretPosition">The position of the caret after the triggering action.</param>
 /// <param name="options">The CompletionOptions that override the default options.</param>
 /// <param name="trigger">The triggering action.</param>
 /// <param name="roles">Optional set of roles associated with the editor state.</param>
 /// <param name="cancellationToken"></param>
 internal virtual Task <CompletionList> GetCompletionsAsync(
     Document document,
     int caretPosition,
     CompletionOptions options,
     OptionSet passThroughOptions,
     CompletionTrigger trigger           = default,
     ImmutableHashSet <string>?roles     = null,
     CancellationToken cancellationToken = default)
 {
     return(GetCompletionsWithAvailabilityOfExpandedItemsAsync(document, caretPosition, options, passThroughOptions, trigger, roles, cancellationToken));
 }
Exemple #3
0
 /// <summary>
 /// Returns true if the character recently inserted or deleted in the text should trigger completion.
 /// </summary>
 /// <param name="project">The project containing the document and text</param>
 /// <param name="languageServices">Language services</param>
 /// <param name="text">The document text to trigger completion within </param>
 /// <param name="caretPosition">The position of the caret after the triggering action.</param>
 /// <param name="trigger">The potential triggering action.</param>
 /// <param name="options">Options.</param>
 /// <param name="roles">Optional set of roles associated with the editor state.</param>
 /// <remarks>
 /// We pass the project here to retrieve information about the <see cref="Project.AnalyzerReferences"/>,
 /// <see cref="WorkspaceKind"/> and <see cref="Project.Language"/> which are fast operations.
 /// It should not be used for syntactic or semantic operations.
 /// </remarks>
 internal virtual bool ShouldTriggerCompletion(
     Project?project,
     HostLanguageServices languageServices,
     SourceText text,
     int caretPosition,
     CompletionTrigger trigger,
     CompletionOptions options,
     ImmutableHashSet <string>?roles = null)
 {
     Debug.Fail("Backward compat only, should not be called");
     return(ShouldTriggerCompletion(text, caretPosition, trigger, roles, options.ToSet(languageServices.Language)));
 }
        /// <summary>
        /// Gets the completions available at the caret position.
        /// </summary>
        /// <param name="document">The document that completion is occurring within.</param>
        /// <param name="caretPosition">The position of the caret after the triggering action.</param>
        /// <param name="options">The CompletionOptions that override the default options.</param>
        /// <param name="trigger">The triggering action.</param>
        /// <param name="roles">Optional set of roles associated with the editor state.</param>
        /// <param name="cancellationToken"></param>
        internal virtual async Task <CompletionList> GetCompletionsAsync(
            Document document,
            int caretPosition,
            CompletionOptions options,
            OptionSet passThroughOptions,
            CompletionTrigger trigger           = default,
            ImmutableHashSet <string>?roles     = null,
            CancellationToken cancellationToken = default)
        {
#pragma warning disable RS0030 // Do not use banned APIs
            return(await GetCompletionsAsync(document, caretPosition, trigger, roles, passThroughOptions, cancellationToken).ConfigureAwait(false) ?? CompletionList.Empty);

#pragma warning restore
        }
Exemple #5
0
        /// <summary>
        /// Gets the completions available at the caret position, with additional info indicates
        /// whether expander items are available.
        /// </summary>
        /// <remarks>
        /// expandItemsAvailable is true when expanded items are returned or can be provided upon request.
        /// </remarks>
        internal virtual async Task <(CompletionList?completionList, bool expandItemsAvailable)> GetCompletionsInternalAsync(
            Document document,
            int caretPosition,
            CompletionOptions options,
            CompletionTrigger trigger           = default,
            ImmutableHashSet <string>?roles     = null,
            CancellationToken cancellationToken = default)
        {
#pragma warning disable RS0030 // Do not use banned APIs
            var completionList = await GetCompletionsAsync(document, caretPosition, trigger, roles, options.ToSet(document.Project.Language), cancellationToken).ConfigureAwait(false);

            return(completionList, false);

#pragma warning restore
        }
Exemple #6
0
        private async Task <CompletionList> GetCompletionsWithAvailabilityOfExpandedItemsAsync(
            Document document,
            int caretPosition,
            CompletionOptions options,
            OptionSet passThroughOptions,
            CompletionTrigger trigger,
            ImmutableHashSet <string>?roles,
            CancellationToken cancellationToken)
        {
            // We don't need SemanticModel here, just want to make sure it won't get GC'd before CompletionProviders are able to get it.
            (document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false);

            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var defaultItemSpan = GetDefaultCompletionListSpan(text, caretPosition);

            var providers = _providerManager.GetFilteredProviders(document.Project, roles, trigger, options);

            // Phase 1: Completion Providers decide if they are triggered based on textual analysis
            // Phase 2: Completion Providers use syntax to confirm they are triggered, or decide they are not actually triggered and should become an augmenting provider
            // Phase 3: Triggered Providers are asked for items
            // Phase 4: If any items were provided, all augmenting providers are asked for items
            // This allows a provider to be textually triggered but later decide to be an augmenting provider based on deeper syntactic analysis.

            var triggeredProviders = GetTriggeredProviders(document, providers, caretPosition, options, trigger, roles, text);

            var additionalAugmentingProviders = await GetAugmentingProviders(document, triggeredProviders, caretPosition, trigger, options, cancellationToken).ConfigureAwait(false);

            triggeredProviders = triggeredProviders.Except(additionalAugmentingProviders).ToImmutableArray();

            // PERF: Many CompletionProviders compute identical contexts. This actually shows up on the 2-core typing test.
            // so we try to share a single SyntaxContext based on document/caretPosition among all providers to reduce repeat computation.
            var sharedContext = new SharedSyntaxContextsWithSpeculativeModel(document, caretPosition);

            // Now, ask all the triggered providers, in parallel, to populate a completion context.
            // Note: we keep any context with items *or* with a suggested item.
            var triggeredContexts = await ComputeNonEmptyCompletionContextsAsync(
                document, caretPosition, trigger, options, defaultItemSpan, triggeredProviders, sharedContext, cancellationToken).ConfigureAwait(false);

            // Nothing to do if we didn't even get any regular items back (i.e. 0 items or suggestion item only.)
            if (!triggeredContexts.Any(static cc => cc.Items.Count > 0))
        /// <summary>
        /// Returns true if the character recently inserted or deleted in the text should trigger completion.
        /// </summary>
        /// <param name="project">The project containing the document and text</param>
        /// <param name="languageServices">Language services</param>
        /// <param name="text">The document text to trigger completion within </param>
        /// <param name="caretPosition">The position of the caret after the triggering action.</param>
        /// <param name="trigger">The potential triggering action.</param>
        /// <param name="options">Options.</param>
        /// <param name="passThroughOptions">Options originating either from external caller of the <see cref="CompletionService"/> or set externally to <see cref="Solution.Options"/>.</param>
        /// <param name="roles">Optional set of roles associated with the editor state.</param>
        /// <remarks>
        /// We pass the project here to retrieve information about the <see cref="Project.AnalyzerReferences"/>,
        /// <see cref="WorkspaceKind"/> and <see cref="Project.Language"/> which are fast operations.
        /// It should not be used for syntactic or semantic operations.
        /// </remarks>
        internal virtual bool ShouldTriggerCompletion(
            Project?project,
            HostLanguageServices languageServices,
            SourceText text,
            int caretPosition,
            CompletionTrigger trigger,
            CompletionOptions options,
            OptionSet passThroughOptions,
            ImmutableHashSet <string>?roles = null)
        {
            if (!options.TriggerOnTyping)
            {
                return(false);
            }

            if (trigger.Kind == CompletionTriggerKind.Deletion && SupportsTriggerOnDeletion(options))
            {
                return(char.IsLetterOrDigit(trigger.Character) || trigger.Character == '.');
            }

            var providers = _providerManager.GetFilteredProviders(project, roles, trigger, options);

            return(providers.Any(p => p.ShouldTriggerCompletion(languageServices, text, caretPosition, trigger, options, passThroughOptions)));
        }
 internal abstract CompletionRules GetRules(CompletionOptions options);
 /// <summary>
 /// Gets the description of the item.
 /// </summary>
 /// <param name="document">This will be the  original document that
 /// <paramref name="item"/> was created against.</param>
 /// <param name="item">The item to get the description for.</param>
 /// <param name="options">Completion options</param>
 /// <param name="displayOptions">Display options</param>
 /// <param name="cancellationToken"></param>
 internal abstract Task <CompletionDescription?> GetDescriptionAsync(Document document, CompletionItem item, CompletionOptions options, SymbolDescriptionOptions displayOptions, CancellationToken cancellationToken = default);
Exemple #10
0
 internal override bool ShouldTriggerCompletion(HostLanguageServices languageServices, SourceText text, int caretPosition, CompletionTrigger trigger, CompletionOptions options, OptionSet passThroughOptions)
 => ShouldTriggerCompletionImpl(text, caretPosition, trigger, options);
            static async Task <ImmutableArray <CompletionProvider> > GetAugmentingProviders(
                Document document, ImmutableArray <CompletionProvider> triggeredProviders, int caretPosition, CompletionTrigger trigger, CompletionOptions options, CancellationToken cancellationToken)
            {
                var additionalAugmentingProviders = ArrayBuilder <CompletionProvider> .GetInstance(triggeredProviders.Length);

                if (trigger.Kind == CompletionTriggerKind.Insertion)
                {
                    foreach (var provider in triggeredProviders)
                    {
                        if (!await provider.IsSyntacticTriggerCharacterAsync(document, caretPosition, trigger, options, cancellationToken).ConfigureAwait(false))
                        {
                            additionalAugmentingProviders.Add(provider);
                        }
                    }
                }

                return(additionalAugmentingProviders.ToImmutableAndFree());
            }
        private protected async Task <CompletionList> GetCompletionsWithAvailabilityOfExpandedItemsAsync(
            Document document,
            int caretPosition,
            CompletionOptions options,
            OptionSet passThroughOptions,
            CompletionTrigger trigger,
            ImmutableHashSet <string>?roles,
            CancellationToken cancellationToken)
        {
            // We don't need SemanticModel here, just want to make sure it won't get GC'd before CompletionProviders are able to get it.
            (document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false);

            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var defaultItemSpan = GetDefaultCompletionListSpan(text, caretPosition);

            var providers = _providerManager.GetFilteredProviders(document.Project, roles, trigger, options);

            // Phase 1: Completion Providers decide if they are triggered based on textual analysis
            // Phase 2: Completion Providers use syntax to confirm they are triggered, or decide they are not actually triggered and should become an augmenting provider
            // Phase 3: Triggered Providers are asked for items
            // Phase 4: If any items were provided, all augmenting providers are asked for items
            // This allows a provider to be textually triggered but later decide to be an augmenting provider based on deeper syntactic analysis.

            var triggeredProviders = GetTriggeredProviders(document, providers, caretPosition, options, trigger, roles, text);

            var additionalAugmentingProviders = await GetAugmentingProviders(document, triggeredProviders, caretPosition, trigger, options, cancellationToken).ConfigureAwait(false);

            triggeredProviders = triggeredProviders.Except(additionalAugmentingProviders).ToImmutableArray();

            // Now, ask all the triggered providers, in parallel, to populate a completion context.
            // Note: we keep any context with items *or* with a suggested item.
            var triggeredContexts = await ComputeNonEmptyCompletionContextsAsync(
                document, caretPosition, trigger, options, defaultItemSpan, triggeredProviders, cancellationToken).ConfigureAwait(false);

            // Nothing to do if we didn't even get any regular items back (i.e. 0 items or suggestion item only.)
            if (!triggeredContexts.Any(cc => cc.Items.Count > 0))
            {
                return(CompletionList.Empty);
            }

            // See if there were completion contexts provided that were exclusive. If so, then
            // that's all we'll return.
            var exclusiveContexts = triggeredContexts.Where(t => t.IsExclusive).ToImmutableArray();

            if (!exclusiveContexts.IsEmpty)
            {
                return(MergeAndPruneCompletionLists(exclusiveContexts, defaultItemSpan, options, isExclusive: true));
            }

            // Great!  We had some items.  Now we want to see if any of the other providers
            // would like to augment the completion list.  For example, we might trigger
            // enum-completion on space.  If enum completion results in any items, then
            // we'll want to augment the list with all the regular symbol completion items.
            var augmentingProviders = providers.Except(triggeredProviders).ToImmutableArray();

            var augmentingContexts = await ComputeNonEmptyCompletionContextsAsync(
                document, caretPosition, trigger, options, defaultItemSpan, augmentingProviders, cancellationToken).ConfigureAwait(false);

            GC.KeepAlive(semanticModel);

            // Providers are ordered, but we processed them in our own order.  Ensure that the
            // groups are properly ordered based on the original providers.
            var completionProviderToIndex = GetCompletionProviderToIndex(providers);
            var allContexts = triggeredContexts.Concat(augmentingContexts)
                              .Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);

            return(MergeAndPruneCompletionLists(allContexts, defaultItemSpan, options, isExclusive: false));

            ImmutableArray <CompletionProvider> GetTriggeredProviders(
                Document document, ConcatImmutableArray <CompletionProvider> providers, int caretPosition, CompletionOptions options, CompletionTrigger trigger, ImmutableHashSet <string>?roles, SourceText text)
            {
                switch (trigger.Kind)
                {
                case CompletionTriggerKind.Insertion:
                case CompletionTriggerKind.Deletion:

                    if (ShouldTriggerCompletion(document.Project, document.Project.LanguageServices, text, caretPosition, trigger, options, passThroughOptions, roles))
                    {
                        var triggeredProviders = providers.Where(p => p.ShouldTriggerCompletion(document.Project.LanguageServices, text, caretPosition, trigger, options, passThroughOptions)).ToImmutableArrayOrEmpty();

                        Debug.Assert(ValidatePossibleTriggerCharacterSet(trigger.Kind, triggeredProviders, document, text, caretPosition, options));
                        return(triggeredProviders.IsEmpty ? providers.ToImmutableArray() : triggeredProviders);
                    }

                    return(ImmutableArray <CompletionProvider> .Empty);

                default:
                    return(providers.ToImmutableArray());
                }
            }
 /// <summary>
 /// For backwards API compat only, should not be called.
 /// </summary>
 public sealed override bool ShouldTriggerCompletion(SourceText text, int caretPosition, CompletionTrigger trigger, OptionSet options)
 {
     Debug.Fail("For backwards API compat only, should not be called");
     return ShouldTriggerCompletionImpl(text, caretPosition, trigger, CompletionOptions.From(options, Language));
 }
        /// <summary>
        /// Gets the description of the item.
        /// </summary>
        /// <param name="document">This will be the  original document that
        /// <paramref name="item"/> was created against.</param>
        /// <param name="item">The item to get the description for.</param>
        /// <param name="options">Completion options</param>
        /// <param name="displayOptions">Display options</param>
        /// <param name="cancellationToken"></param>
        internal virtual async Task <CompletionDescription?> GetDescriptionAsync(Document document, CompletionItem item, CompletionOptions options, SymbolDescriptionOptions displayOptions, CancellationToken cancellationToken = default)
        {
            var provider = GetProvider(item, document.Project);

            if (provider is null)
            {
                return(CompletionDescription.Empty);
            }

            // We don't need SemanticModel here, just want to make sure it won't get GC'd before CompletionProviders are able to get it.
            (document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false);

            var description = await provider.GetDescriptionAsync(document, item, options, displayOptions, cancellationToken).ConfigureAwait(false);

            GC.KeepAlive(semanticModel);
            return(description);
        }
 internal virtual bool SupportsTriggerOnDeletion(CompletionOptions options)
 => options.TriggerOnDeletion == true;
Exemple #16
0
 /// <summary>
 /// Gets the description of the item.
 /// </summary>
 /// <param name="document">This will be the  original document that
 /// <paramref name="item"/> was created against.</param>
 /// <param name="item">The item to get the description for.</param>
 /// <param name="cancellationToken"></param>
 public Task <CompletionDescription?> GetDescriptionAsync(
     Document document,
     CompletionItem item,
     CancellationToken cancellationToken = default)
 => GetDescriptionAsync(document, item, CompletionOptions.From(document.Project), SymbolDescriptionOptions.From(document.Project), cancellationToken);