static CompletionItem GetAppropriateAttributeItem(CompletionItem attributeItem, bool isCaseSensitive)
            {
                if (attributeItem.DisplayText.TryGetWithoutAttributeSuffix(isCaseSensitive: isCaseSensitive, out var attributeNameWithoutSuffix))
                {
                    // We don't want to cache this item.
                    return(ImportCompletionItem.CreateAttributeItemWithoutSuffix(attributeItem, attributeNameWithoutSuffix, CompletionItemFlags.Expanded));
                }

                return(attributeItem);
            }
 static void AddItems(ImmutableArray <CompletionItem> items, CompletionContext completionContext, HashSet <string> namespacesInScope, TelemetryCounter counter)
 {
     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++;;
         }
     }
 }
            public void AddItem(INamedTypeSymbol symbol, string containingNamespace, bool isPublic)
            {
                // We want to cache items with EditoBrowsableState == Advanced regardless of current "hide adv members" option value
                var(isBrowsable, isEditorBrowsableStateAdvanced) = symbol.IsEditorBrowsableWithState(
                    hideAdvancedMembers: false,
                    _editorBrowsableInfo.Compilation,
                    _editorBrowsableInfo);

                if (!isBrowsable)
                {
                    // Hide this item from completion
                    return;
                }

                var isGeneric = symbol.Arity > 0;

                // Need to determine if a type is an attribute up front since we want to filter out
                // non-attribute types when in attribute context. We can't do this lazily since we don't hold
                // on to symbols. However, the cost of calling `IsAttribute` on every top-level type symbols
                // is prohibitively high, so we opt for the heuristic that would do the simple textual "Attribute"
                // suffix check first, then the more expensive symbolic check. As a result, all unimported
                // attribute types that don't have "Attribute" suffix would be filtered out when in attribute context.
                var isAttribute = symbol.Name.HasAttributeSuffix(isCaseSensitive: false) && symbol.IsAttribute();

                var item = ImportCompletionItem.Create(
                    symbol.Name,
                    symbol.Arity,
                    containingNamespace,
                    symbol.GetGlyph(),
                    _genericTypeSuffix,
                    CompletionItemFlags.CachedAndExpanded,
                    extensionMethodData: null);

                if (isPublic)
                {
                    _publicItemCount++;
                }

                _itemsBuilder.Add(new TypeImportCompletionItemInfo(item, isPublic, isGeneric, isAttribute, isEditorBrowsableStateAdvanced));
            }
 internal override Task <CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CompletionOptions options, SymbolDescriptionOptions displayOptions, CancellationToken cancellationToken)
 => ImportCompletionItem.GetCompletionDescriptionAsync(document, item, displayOptions, cancellationToken);
        public override async Task <CompletionChange> GetChangeAsync(
            Document document, CompletionItem completionItem, char?commitKey, CancellationToken cancellationToken)
        {
            var containingNamespace          = ImportCompletionItem.GetContainingNamespace(completionItem);
            var provideParenthesisCompletion = await ShouldProvideParenthesisCompletionAsync(
                document,
                completionItem,
                commitKey,
                cancellationToken).ConfigureAwait(false);

            var insertText = completionItem.DisplayText;

            if (provideParenthesisCompletion)
            {
                insertText += "()";
                CompletionProvidersLogger.LogCustomizedCommitToAddParenthesis(commitKey);
            }

            if (await ShouldCompleteWithFullyQualifyTypeName().ConfigureAwait(false))
            {
                var completionText = $"{containingNamespace}.{insertText}";
                return(CompletionChange.Create(new TextChange(completionItem.Span, completionText)));
            }

            // Find context node so we can use it to decide where to insert using/imports.
            var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

            var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            var addImportContextNode = root.FindToken(completionItem.Span.Start, findInsideTrivia: true).Parent;

            // Add required using/imports directive.
            var addImportService = document.GetRequiredLanguageService <IAddImportsService>();
            var generator        = document.GetRequiredLanguageService <SyntaxGenerator>();

            var addImportsOptions = await AddImportPlacementOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false);

            var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false);

            var importNode = CreateImport(document, containingNamespace);

            var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);

            var rootWithImport     = addImportService.AddImport(compilation, root, addImportContextNode !, importNode, generator, addImportsOptions, cancellationToken);
            var documentWithImport = document.WithSyntaxRoot(rootWithImport);
            // This only formats the annotated import we just added, not the entire document.
            var formattedDocumentWithImport = await Formatter.FormatAsync(documentWithImport, Formatter.Annotation, formattingOptions, cancellationToken).ConfigureAwait(false);

            using var _ = ArrayBuilder <TextChange> .GetInstance(out var builder);

            // Get text change for add import
            var importChanges = await formattedDocumentWithImport.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false);

            builder.AddRange(importChanges);

            // Create text change for complete type name.
            //
            // Note: Don't try to obtain TextChange for completed type name by replacing the text directly,
            //       then use Document.GetTextChangesAsync on document created from the changed text. This is
            //       because it will do a diff and return TextChanges with minimum span instead of actual
            //       replacement span.
            //
            //       For example: If I'm typing "asd", the completion provider could be triggered after "a"
            //       is typed. Then if I selected type "AsnEncodedData" to commit, by using the approach described
            //       above, we will get a TextChange of "AsnEncodedDat" with 0 length span, instead of a change of
            //       the full display text with a span of length 1. This will later mess up span-tracking and end up
            //       with "AsnEncodedDatasd" in the code.
            builder.Add(new TextChange(completionItem.Span, insertText));

            // Then get the combined change
            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var newText = text.WithChanges(builder);

            var changes = builder.ToImmutable();
            var change  = Utilities.Collapse(newText, changes);

            return(CompletionChange.Create(change, changes));

            async Task <bool> ShouldCompleteWithFullyQualifyTypeName()
            {
                if (!IsAddingImportsSupported(document))
                {
                    return(true);
                }

                // We might need to qualify unimported types to use them in an import directive, because they only affect members of the containing
                // import container (e.g. namespace/class/etc. declarations).
                //
                // For example, `List` and `StringBuilder` both need to be fully qualified below:
                //
                //      using CollectionOfStringBuilders = System.Collections.Generic.List<System.Text.StringBuilder>;
                //
                // However, if we are typing in an C# using directive that is inside a nested import container (i.e. inside a namespace declaration block),
                // then we can add an using in the outer import container instead (this is not allowed in VB).
                //
                // For example:
                //
                //      using System.Collections.Generic;
                //      using System.Text;
                //
                //      namespace Foo
                //      {
                //          using CollectionOfStringBuilders = List<StringBuilder>;
                //      }
                //
                // Here we will always choose to qualify the unimported type, just to be consistent and keeps things simple.
                return(await IsInImportsDirectiveAsync(document, completionItem.Span.Start, cancellationToken).ConfigureAwait(false));
            }
        }
 protected override Task <CompletionDescription> GetDescriptionWorkerAsync(Document document, CompletionItem item, CancellationToken cancellationToken)
 => ImportCompletionItem.GetCompletionDescriptionAsync(document, item, cancellationToken);
        public ImmutableArray <CompletionItem> GetItemsForContext(
            Compilation originCompilation,
            string language,
            string genericTypeSuffix,
            bool isAttributeContext,
            bool isCaseSensitive,
            bool hideAdvancedMembers)
        {
            if (AssemblySymbolKey.Resolve(originCompilation).Symbol is not IAssemblySymbol assemblySymbol)
            {
                return(ImmutableArray <CompletionItem> .Empty);
            }

            var isSameLanguage     = Language == language;
            var isInternalsVisible = originCompilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(assemblySymbol);

            using var _ = ArrayBuilder <CompletionItem> .GetInstance(out var builder);

            // PERF: try set the capacity upfront to avoid allocation from Resize
            if (!isAttributeContext)
            {
                if (isInternalsVisible)
                {
                    builder.EnsureCapacity(ItemInfos.Length);
                }
                else
                {
                    builder.EnsureCapacity(PublicItemCount);
                }
            }

            foreach (var info in ItemInfos)
            {
                if (!info.IsPublic && !isInternalsVisible)
                {
                    continue;
                }

                // Option to show advanced members is false so we need to exclude them.
                if (hideAdvancedMembers && info.IsEditorBrowsableStateAdvanced)
                {
                    continue;
                }

                var item = info.Item;

                if (isAttributeContext)
                {
                    // Don't show non attribute item in attribute context
                    if (!info.IsAttribute)
                    {
                        continue;
                    }

                    // We are in attribute context, will not show or complete with "Attribute" suffix.
                    item = GetAppropriateAttributeItem(info.Item, isCaseSensitive);
                }

                // C# and VB the display text is different for generics, i.e. <T> and (Of T). For simpllicity, we only cache for one language.
                // But when we trigger in a project with different language than when the cache entry was created for, we will need to
                // change the generic suffix accordingly.
                if (!isSameLanguage && info.IsGeneric)
                {
                    // We don't want to cache this item.
                    item = ImportCompletionItem.CreateItemWithGenericDisplaySuffix(item, genericTypeSuffix);
                }

                builder.Add(item);
            }

            return(builder.ToImmutable());