Example #1
0
        private VSCompletionItem Convert(
            Document document,
            RoslynCompletionItem roslynItem,
            CompletionService completionService,
            Dictionary <string, AsyncCompletionData.CompletionFilter> filterCache)
        {
            var imageId = roslynItem.Tags.GetFirstGlyph().GetImageId();
            var filters = GetFilters(roslynItem, filterCache);

            // roslynItem generated by providers can contain an insertionText in a property bag.
            // We will not use it but other providers may need it.
            // We actually will calculate the insertion text once again when called TryCommit.
            if (!roslynItem.Properties.TryGetValue(InsertionText, out var insertionText))
            {
                insertionText = roslynItem.DisplayText;
            }

            var supportedPlatforms = SymbolCompletionItem.GetSupportedPlatforms(roslynItem, document.Project.Solution.Workspace);
            var attributeImages    = supportedPlatforms != null ? s_WarningImageAttributeImagesArray : ImmutableArray <ImageElement> .Empty;

            var item = new VSCompletionItem(
                displayText: roslynItem.GetEntireDisplayText(),
                source: this,
                icon: new ImageElement(new ImageId(imageId.Guid, imageId.Id), roslynItem.DisplayText),
                filters: filters,
                suffix: roslynItem.InlineDescription, // InlineDescription will be right-aligned in the selection popup
                insertText: insertionText,
                sortText: roslynItem.SortText,
                filterText: roslynItem.FilterText,
                attributeIcons: attributeImages);

            item.Properties.AddProperty(RoslynItem, roslynItem);
            return(item);
        }
Example #2
0
            static bool ShouldBeFilteredOutOfCompletionList(VSCompletionItem item, ImmutableArray <CompletionFilter> activeNonExpanderFilters)
            {
                if (item.Filters.Any(filter => activeNonExpanderFilters.Contains(filter)))
                {
                    return(false);
                }

                return(true);
            }
Example #3
0
        public Task <CompletionContext> GetCompletionContextAsync(IAsyncCompletionSession session, CompletionTrigger trigger, SnapshotPoint triggerLocation, SnapshotSpan applicableToSpan, CancellationToken token)
        {
            //session.Properties["LineNumber"] = triggerLocation.GetContainingLine().LineNumber;
            var point = applicableToSpan.Start;
            var text  = textView.TextBuffer.CurrentSnapshot.GetText(0, point.Position);

            return(Task.Run(() =>
            {
                try
                {
                    // read XML up to the cursor
                    var info = XmlParser.Read(text);
                    if (info.Mode == CompletionMode.Class)
                    {
                        var prevPoint = point - 1;
                        var prevCh = prevPoint.GetChar();
                        if (prevCh != '<' && prevCh != '.')
                        {
                            return CompletionContext.Empty;
                        }
                    }

                    var nodes = info.Nodes;
                    var ns = nodes.SelectMany(r => r.Namespaces ?? Enumerable.Empty <CompletionNamespace>());
                    var path = nodes.Where(r => r.Mode == CompletionMode.Class).Select(r => r.Name).ToList();
                    var last = nodes.LastOrDefault();

                    // get available completion items
                    var items = Designer.Completion.Completion.GetCompletionItems(ns, info.Mode, path, last);

                    // translate to VS completions
                    var completionList = new List <mvli.AsyncCompletion.Data.CompletionItem>();
                    foreach (var cls in items.OrderBy(r => r.Name))
                    {
                        var displayText = cls.Name;
                        var item = new mvli.AsyncCompletion.Data.CompletionItem(displayText,
                                                                                source: this,
                                                                                filters: ImmutableArray <CompletionFilter> .Empty,
                                                                                icon: GetGlyph(cls.Type),
                                                                                suffix: cls.Suffix ?? string.Empty,
                                                                                attributeIcons: ImmutableArray <ImageElement> .Empty,
                                                                                insertText: displayText, sortText: displayText, filterText: displayText
                                                                                );
                        item.Properties["eto"] = cls;
                        completionList.Add(item);
                    }

                    return new CompletionContext(completionList.ToImmutableArray(), null, InitialSelectionHint.RegularSelection);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Error doing autocomplete: {ex}");
                    throw;
                }
            }));
        }
Example #4
0
        internal static bool TryGetInitialTriggerLocation(VSCompletionItem item, out SnapshotPoint initialTriggerLocation)
        {
            if (item.Properties.TryGetProperty(CompletionSource.TriggerLocation, out initialTriggerLocation))
            {
                return(true);
            }

            initialTriggerLocation = default;
            return(false);
        }
Example #5
0
        private static bool ShouldBeFilteredOutOfCompletionList(VSCompletionItem item, ImmutableArray <CompletionFilter> activeFilters)
        {
            foreach (var itemFilter in item.Filters)
            {
                if (activeFilters.Contains(itemFilter))
                {
                    return(false);
                }
            }

            return(true);
        }
Example #6
0
        private static RoslynCompletionItem GetOrAddRoslynCompletionItem(VSCompletionItem vsItem)
        {
            if (!vsItem.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem))
            {
                roslynItem = RoslynCompletionItem.Create(
                    displayText: vsItem.DisplayText,
                    filterText: vsItem.FilterText,
                    sortText: vsItem.SortText,
                    displayTextSuffix: vsItem.Suffix);

                vsItem.Properties.AddProperty(CompletionSource.RoslynItem, roslynItem);
            }

            return(roslynItem);
        }
Example #7
0
        private VSCompletionItem Convert(
            Document document,
            RoslynCompletionItem roslynItem)
        {
            if (roslynItem.IsCached && s_roslynItemToVsItem.TryGetValue(roslynItem, out var vsItem))
            {
                return(vsItem);
            }

            var imageId = roslynItem.Tags.GetFirstGlyph().GetImageId();
            var filters = GetFilters(roslynItem);

            // roslynItem generated by providers can contain an insertionText in a property bag.
            // We will not use it but other providers may need it.
            // We actually will calculate the insertion text once again when called TryCommit.
            if (!roslynItem.Properties.TryGetValue(InsertionText, out var insertionText))
            {
                insertionText = roslynItem.DisplayText;
            }

            var supportedPlatforms = SymbolCompletionItem.GetSupportedPlatforms(roslynItem, document.Project.Solution.Workspace);
            var attributeImages    = supportedPlatforms != null ? s_WarningImageAttributeImagesArray : ImmutableArray <ImageElement> .Empty;

            var item = new VSCompletionItem(
                displayText: roslynItem.GetEntireDisplayText(),
                source: this,
                icon: new ImageElement(new ImageId(imageId.Guid, imageId.Id), roslynItem.DisplayText),
                filters: filters,
                suffix: roslynItem.InlineDescription, // InlineDescription will be right-aligned in the selection popup
                insertText: insertionText,
                sortText: roslynItem.SortText,
                filterText: roslynItem.FilterText,
                attributeIcons: attributeImages);

            item.Properties.AddProperty(RoslynItem, roslynItem);

            // It doesn't make sense to cache VS item for those Roslyn items created from scratch for each session,
            // since CWT uses object identity for comparison.
            if (roslynItem.IsCached)
            {
                s_roslynItemToVsItem.Add(roslynItem, item);
            }

            return(item);
        }
Example #8
0
            static bool ShouldBeFilteredOutOfExpandedCompletionList(VSCompletionItem item, ImmutableArray <CompletionFilter> unselectedExpanders)
            {
                var associatedWithUnselectedExpander = false;

                foreach (var itemFilter in item.Filters)
                {
                    if (itemFilter is CompletionExpander)
                    {
                        if (!unselectedExpanders.Contains(itemFilter))
                        {
                            // If any of the associated expander is selected, the item should be included in the expanded list.
                            return(false);
                        }

                        associatedWithUnselectedExpander = true;
                    }
                }

                // at this point, the item either:
                // 1. has no expander filter, therefore should be included
                // 2. or, all associated expanders are unselected, therefore should be excluded
                return(associatedWithUnselectedExpander);
            }
Example #9
0
        private CompletionItem CreateCompletion(
            Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem item,
            Func <Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem, Task <Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem> > resolver,
            SnapshotPoint triggerLocation,
            Dictionary <CompletionItemKind, CompletionFilter> completionFilters
            )
        {
            var completionImage = GetCompletionImage(item.Kind);

            var completionItemFilters = ImmutableArray <CompletionFilter> .Empty;

            if (completionFilters.TryGetValue(item.Kind, out CompletionFilter filter))
            {
                completionItemFilters = ImmutableArray.Create <CompletionFilter>(new CompletionFilter[] { filter });
            }

            var completionItem = new CompletionItem(item.Label, this, completionImage, completionItemFilters, string.Empty, item.InsertText ?? item.Label, item.SortText ?? item.Label, item.FilterText ?? item.Label, ImmutableArray <ImageElement> .Empty);

            completionItem.Properties.AddProperty(ResolvePropertyKey, resolver);
            completionItem.Properties.AddProperty(ProtocolItemKey, item);
            completionItem.Properties.AddProperty(TriggerPointKey, triggerLocation);

            return(completionItem);
        }
Example #10
0
        public CommitResult TryCommit(IAsyncCompletionSession session, ITextBuffer buffer, CompletionItem item, char typedChar, CancellationToken token)
        {
            if (item.Properties.TryGetProperty(AsyncCompletionSource.ProtocolItemKey, out LSP.CompletionItem protocolItem) &&
                item.Properties.TryGetProperty(AsyncCompletionSource.TriggerPointKey, out SnapshotPoint triggerLocation))
            {
                var commitArray = protocolItem.CommitCharacters ?? new string[] { }; // Commit chars are normally empty

                // Tab, Enter and programmatical command should always commit regardless of commit characters on individual items.
                if (typedChar != '\0' &&
                    typedChar != '\t' &&
                    typedChar != '\n' &&
                    commitArray != null)
                {
                    bool commitCharacterFound = false;
                    for (var n = 0; n < commitArray.Length; n++)
                    {
                        var commitString = commitArray[n];
                        if (!string.IsNullOrEmpty(commitString) &&
                            commitString[0] == typedChar)
                        {
                            commitCharacterFound = true;
                        }
                    }

                    if (!commitCharacterFound)
                    {
                        return(new CommitResult(isHandled: false, behavior: CommitBehavior.CancelCommit));
                    }
                }

                // Contract with Roslyn: if an item has no insert text and no text edit,
                // attempt to resolve it to get these values.
                // We don't call resolve for all items because it's a network call on a typing hot path.
                if (protocolItem.InsertText == null && protocolItem.TextEdit == null &&
                    item.Properties.TryGetProperty(ResolvePropertyKey, out Func <LSP.CompletionItem, CancellationToken, Task <LSP.CompletionItem> > resolver) &&
                    resolver != null)
                {
                    try {
                        ThreadHelper.JoinableTaskFactory.Run(async() => {
                            await this.ResolveCompletionItemAsync(item, token);
                        });
                    } catch (Exception ex) when(!(ex is OperationCanceledException))
                    {
                        // We have not received the resolved item due to an error.
                        return(new CommitResult(isHandled: false, behavior: CommitBehavior.CancelCommit));
                    }

                    if (token.IsCancellationRequested)
                    {
                        // We have not received the resolved item due to a timeout.
                        return(new CommitResult(isHandled: false, behavior: CommitBehavior.CancelCommit));
                    }
                }

                if (protocolItem.TextEdit != null || protocolItem.AdditionalTextEdits != null)
                {
                    // Completion text edits are computed when the completion session is first triggered. The lines typed
                    // after the completion session was started need to be deleted to revert the document to its original state.
                    var caretPositionAtBuffer = session.TextView.GetCaretPointAtSubjectBuffer(buffer);
                    if (caretPositionAtBuffer.HasValue)
                    {
                        var deleteTextLength = caretPositionAtBuffer.Value.Position - triggerLocation.Position;
                        if (deleteTextLength > 0)
                        {
                            var deleteSpan = new Span(triggerLocation.Position, deleteTextLength);
                            buffer.Delete(deleteSpan);
                        }

                        if (protocolItem.TextEdit != null)
                        {
                            Utilities.ApplyTextEdit(protocolItem.TextEdit, triggerLocation.Snapshot, buffer);
                        }
                        else if (protocolItem.InsertText != null)
                        {
                            buffer.Replace(session.ApplicableToSpan.GetSpan(buffer.CurrentSnapshot), protocolItem.InsertText);
                        }
                        else if (protocolItem.Label != null)
                        {
                            buffer.Replace(session.ApplicableToSpan.GetSpan(buffer.CurrentSnapshot), protocolItem.Label);
                        }

                        if (protocolItem.AdditionalTextEdits != null)
                        {
                            Utilities.ApplyTextEdits(protocolItem.AdditionalTextEdits, triggerLocation.Snapshot, buffer);
                        }

                        this.textView.Caret.EnsureVisible();

                        this.ExecuteCompletionCommand(languageClient, protocolItem, token);
                        return(CommitResult.Handled);
                    }
                }

                this.ExecuteCompletionCommand(languageClient, protocolItem, token);
            }

            return(CommitResult.Unhandled);
        }
Example #11
0
        public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, VSCompletionItem item, CancellationToken cancellationToken)
        {
            if (!item.Properties.TryGetProperty(RoslynItem, out RoslynCompletionItem roslynItem) ||
                !session.Properties.TryGetProperty(TriggerSnapshot, out ITextSnapshot triggerSnapshot))
            {
                return(null);
            }

            var document = triggerSnapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return(null);
            }

            var service = document.GetLanguageService <CompletionService>();

            if (service == null)
            {
                return(null);
            }

            var description = await service.GetDescriptionAsync(document, roslynItem, cancellationToken).ConfigureAwait(false);

            var elements = IntelliSense.Helpers.BuildClassifiedTextElements(description.TaggedParts).ToArray();

            if (elements.Length == 0)
            {
                return(new ClassifiedTextElement());
            }
            else if (elements.Length == 1)
            {
                return(elements[0]);
            }
            else
            {
                return(new ContainerElement(ContainerElementStyle.Stacked | ContainerElementStyle.VerticalPadding, elements));
            }
        }
Example #12
0
 public Task <object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token) =>
 item.GetDescriptionAsync(_descriptionBuilder, token);
Example #13
0
        public AsyncCompletionData.CommitResult TryCommit(
            IAsyncCompletionSession session,
            ITextBuffer subjectBuffer,
            VSCompletionItem item,
            char typeChar,
            CancellationToken cancellationToken)
        {
            // We can make changes to buffers. We would like to be sure nobody can change them at the same time.
            AssertIsForeground();

            var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return(CommitResultUnhandled);
            }

            var completionService = document.GetLanguageService <CompletionService>();

            if (completionService == null)
            {
                return(CommitResultUnhandled);
            }

            if (!item.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem))
            {
                // Roslyn should not be called if the item committing was not provided by Roslyn.
                return(CommitResultUnhandled);
            }

            var filterText = session.ApplicableToSpan.GetText(session.ApplicableToSpan.TextBuffer.CurrentSnapshot) + typeChar;

            if (Helpers.IsFilterCharacter(roslynItem, typeChar, filterText))
            {
                // Returning Cancel means we keep the current session and consider the character for further filtering.
                return(new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.CancelCommit));
            }

            var serviceRules = completionService.GetRules();

            // We can be called before for ShouldCommitCompletion. However, that call does not provide rules applied for the completion item.
            // Now we check for the commit charcter in the context of Rules that could change the list of commit characters.

            // Tab, Enter and Null (call invoke commit) are always commit characters.
            if (typeChar != '\t' && typeChar != '\n' && typeChar != '\0' && !IsCommitCharacter(serviceRules, roslynItem, typeChar, filterText))
            {
                // Returning None means we complete the current session with a void commit.
                // The Editor then will try to trigger a new completion session for the character.
                return(new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None));
            }

            if (!session.Properties.TryGetProperty(CompletionSource.TriggerSnapshot, out ITextSnapshot triggerSnapshot))
            {
                // Need the trigger snapshot to calculate the span when the commit changes to be applied.
                // It should be inserted into a property bag within GetCompletionContextAsync for each item created by Roslyn.
                // If not found here, Roslyn should not make a commit.
                return(CommitResultUnhandled);
            }

            if (!session.Properties.TryGetProperty(CompletionSource.CompletionListSpan, out TextSpan completionListSpan))
            {
                return(CommitResultUnhandled);
            }

            var triggerDocument = triggerSnapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (triggerDocument == null)
            {
                return(CommitResultUnhandled);
            }

            // Commit with completion service assumes that null is provided is case of invoke. VS provides '\0' in the case.
            char?commitChar     = typeChar == '\0' ? null : (char?)typeChar;
            var  commitBehavior = Commit(
                triggerDocument, completionService, session.TextView, subjectBuffer, roslynItem,
                completionListSpan, commitChar, triggerSnapshot, serviceRules, filterText, cancellationToken);

            _recentItemsManager.MakeMostRecentItem(roslynItem.DisplayText);
            return(new AsyncCompletionData.CommitResult(isHandled: true, commitBehavior));
        }
 private bool ShouldBeFilteredOutOfCompletionList(VSCompletionItem item)
 => _needToFilter && !item.Filters.Any(static (filter, self) => self._selectedNonExpanderFilters.Contains(filter), this);
Example #15
0
        public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, VSCompletionItem item, CancellationToken cancellationToken)
        {
            if (!item.Properties.TryGetProperty(RoslynItem, out RoslynCompletionItem roslynItem) ||
                !session.Properties.TryGetProperty(TriggerSnapshot, out ITextSnapshot triggerSnapshot))
            {
                return(null);
            }

            var document = triggerSnapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return(null);
            }

            var service = document.GetLanguageService <CompletionService>();

            if (service == null)
            {
                return(null);
            }

            var description = await service.GetDescriptionAsync(document, roslynItem, cancellationToken).ConfigureAwait(false);

            return(IntelliSense.Helpers.BuildClassifiedTextElement(description.TaggedParts));
        }
Example #16
0
        public Task <object> GetDescriptionAsync(IAsyncCompletionSession session, mvli.AsyncCompletion.Data.CompletionItem item, CancellationToken token)
        {
            var etoitem = item.Properties["eto"] as Designer.Completion.CompletionItem;

            return(Task.FromResult <object>(etoitem?.Description));
        }
Example #17
0
 // This is a temporarily method to support preference of IntelliCode items comparing to non-IntelliCode items.
 // We expect that Editor will introduce this support and we will get rid of relying on the "★" then.
 internal static bool IsPreferredItem(this VSCompletionItem completionItem)
 => completionItem.DisplayText.StartsWith("★");
Example #18
0
        public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token)
        {
            try {
                await this.ResolveCompletionItemAsync(item, token);
            } catch (Exception ex) when(!(ex is OperationCanceledException))
            {
                return(null);
            }

            if (token.IsCancellationRequested)
            {
                return(null);
            }

            if (!item.Properties.TryGetProperty(ProtocolItemKey, out LSP.CompletionItem protocolItem))
            {
                return(null);
            }

            // Support the Detail property, but keep in mind that its use for tooltip will be deprecated soon.
            // We will use Detail property for CompletionItem.Suffix
            if (!string.IsNullOrWhiteSpace(protocolItem.Detail))
            {
                return(protocolItem.Detail);
            }

            // otherwise, parse the Documentation property
            if (protocolItem.Documentation != null)
            {
                var documentationBuilder = new StringBuilder();
                var content = protocolItem.Documentation.Value.Match(
                    s => s,
                    markupContent => {
                    switch (markupContent.Kind)
                    {
                    case LSP.MarkupKind.Markdown:
                        var codeBlocks   = MarkdownUtil.ExtractCodeBlocks(markupContent.Value);
                        var joinedBlocks = string.Join(Environment.NewLine, codeBlocks);
                        if (string.IsNullOrEmpty(joinedBlocks))
                        {
                            return(null);
                        }

                        return(joinedBlocks);

                    case LSP.MarkupKind.PlainText:
                    default:
                        return(markupContent.Value);
                    }
                });

                if (!string.IsNullOrEmpty(content))
                {
                    if (documentationBuilder.Length > 0)
                    {
                        documentationBuilder.AppendLine();
                    }

                    documentationBuilder.Append(content);
                }

                return(documentationBuilder.ToString());
            }

            return(null);
        }
Example #19
0
        public async Task <object> GetDescriptionAsync(IAsyncCompletionSession session, CompletionItem item, CancellationToken token)
        {
            if (item.Properties.TryGetProperty(ResolvePropertyKey, out Func <Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem, Task <Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem> > resolver) &&
                resolver != null &&
                item.Properties.TryGetProperty(ProtocolItemKey, out Microsoft.VisualStudio.LanguageServer.Protocol.CompletionItem unresolvedItem))
            {
                var resolvedItem = await resolver(unresolvedItem);

                if (resolvedItem != null)
                {
                    unresolvedItem.TextEdit            = unresolvedItem.TextEdit ?? resolvedItem.TextEdit;
                    unresolvedItem.AdditionalTextEdits = unresolvedItem.AdditionalTextEdits ?? resolvedItem.AdditionalTextEdits;
                    return(resolvedItem?.Detail);
                }
            }

            return(null);
        }
Example #20
0
        private FilteredCompletionModel HandleNormalFiltering(
            Func <ImmutableArray <RoslynCompletionItem>, string, ImmutableArray <RoslynCompletionItem> > filterMethod,
            string filterText,
            ImmutableArray <CompletionFilterWithState> filters,
            CompletionTriggerKind initialRoslynTriggerKind,
            CompletionFilterReason filterReason,
            char typeChar,
            List <ExtendedFilterResult> itemsInList,
            ImmutableArray <CompletionItemWithHighlight> highlightedList,
            CompletionHelper completionHelper,
            bool hasSuggestedItemOptions)
        {
            // Not deletion.  Defer to the language to decide which item it thinks best
            // matches the text typed so far.

            // Ask the language to determine which of the *matched* items it wants to select.
            var matchingItems = itemsInList.Where(r => r.FilterResult.MatchedFilterText)
                                .Select(t => t.FilterResult.CompletionItem)
                                .AsImmutable();

            var chosenItems = filterMethod(matchingItems, filterText);

            var recentItems = _recentItemsManager.RecentItems;

            // Of the items the service returned, pick the one most recently committed
            var bestItem = GetBestCompletionItemBasedOnMRU(chosenItems, recentItems);
            VSCompletionItem uniqueItem = null;
            int selectedItemIndex       = 0;

            // Determine if we should consider this item 'unique' or not.  A unique item
            // will be automatically committed if the user hits the 'invoke completion'
            // without bringing up the completion list.  An item is unique if it was the
            // only item to match the text typed so far, and there was at least some text
            // typed.  i.e.  if we have "Console.$$" we don't want to commit something
            // like "WriteLine" since no filter text has actually been provided.  HOwever,
            // if "Console.WriteL$$" is typed, then we do want "WriteLine" to be committed.
            if (bestItem != null)
            {
                selectedItemIndex = itemsInList.IndexOf(i => Equals(i.FilterResult.CompletionItem, bestItem));
                if (selectedItemIndex > -1 && bestItem != null && matchingItems.Length == 1 && filterText.Length > 0)
                {
                    uniqueItem = highlightedList[selectedItemIndex].CompletionItem;
                }
            }

            // If we don't have a best completion item yet, then pick the first item from the list.
            var bestOrFirstCompletionItem = bestItem ?? itemsInList.First().FilterResult.CompletionItem;

            // Check that it is a filter symbol. We can be called for a non-filter symbol.
            if (filterReason == CompletionFilterReason.Insertion &&
                !IsPotentialFilterCharacter(typeChar) &&
                !string.IsNullOrEmpty(filterText) &&
                !Helpers.IsFilterCharacter(bestOrFirstCompletionItem, typeChar, filterText))
            {
                return(null);
            }

            bool isHardSelection = IsHardSelection(
                filterText, initialRoslynTriggerKind, bestOrFirstCompletionItem,
                completionHelper, filterReason, recentItems, hasSuggestedItemOptions);

            var updateSelectionHint = isHardSelection ? UpdateSelectionHint.Selected : UpdateSelectionHint.SoftSelected;

            // If no items found above, select the first item.
            if (selectedItemIndex == -1)
            {
                selectedItemIndex = 0;
            }

            return(new FilteredCompletionModel(
                       highlightedList, selectedItemIndex, filters,
                       updateSelectionHint, centerSelection: true, uniqueItem));
        }
Example #21
0
        public AsyncCompletionData.CommitResult TryCommit(
            IAsyncCompletionSession session,
            ITextBuffer subjectBuffer,
            VSCompletionItem item,
            char typeChar,
            CancellationToken cancellationToken)
        {
            // We can make changes to buffers. We would like to be sure nobody can change them at the same time.
            AssertIsForeground();

            var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (document == null)
            {
                return(CommitResultUnhandled);
            }

            var completionService = document.GetLanguageService <CompletionService>();

            if (completionService == null)
            {
                return(CommitResultUnhandled);
            }

            if (!item.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem))
            {
                // Roslyn should not be called if the item committing was not provided by Roslyn.
                return(CommitResultUnhandled);
            }

            var filterText = session.ApplicableToSpan.GetText(session.ApplicableToSpan.TextBuffer.CurrentSnapshot) + typeChar;

            if (Helpers.IsFilterCharacter(roslynItem, typeChar, filterText))
            {
                // Returning Cancel means we keep the current session and consider the character for further filtering.
                return(new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.CancelCommit));
            }

            var serviceRules = completionService.GetRules();

            // We can be called before for ShouldCommitCompletion. However, that call does not provide rules applied for the completion item.
            // Now we check for the commit charcter in the context of Rules that could change the list of commit characters.

            // Tab, Enter and Null (call invoke commit) are always commit characters.
            if (typeChar != '\t' && typeChar != '\n' && typeChar != '\0' && !IsCommitCharacter(serviceRules, roslynItem, typeChar, filterText))
            {
                // Returning None means we complete the current session with a void commit.
                // The Editor then will try to trigger a new completion session for the character.
                return(new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None));
            }

            if (!Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation))
            {
                // Need the trigger snapshot to calculate the span when the commit changes to be applied.
                // They should always be available from VS. Just to be defensive, if it's not found here, Roslyn should not make a commit.
                return(CommitResultUnhandled);
            }

            if (!session.Properties.TryGetProperty(CompletionSource.CompletionListSpan, out TextSpan completionListSpan))
            {
                return(CommitResultUnhandled);
            }

            var triggerDocument = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

            if (triggerDocument == null)
            {
                return(CommitResultUnhandled);
            }

            // Telemetry
            if (session.TextView.Properties.TryGetProperty(CompletionSource.TypeImportCompletionEnabled, out bool isTyperImportCompletionEnabled) && isTyperImportCompletionEnabled)
            {
                AsyncCompletionLogger.LogCommitWithTypeImportCompletionEnabled();

                if (roslynItem.Flags.IsCached())
                {
                    AsyncCompletionLogger.LogCommitOfTypeImportCompletionItem();
                }
            }

            if (session.TextView.Properties.TryGetProperty(CompletionSource.TargetTypeFilterExperimentEnabled, out bool isExperimentEnabled) && isExperimentEnabled)
            {
                // Capture the % of committed completion items that would have appeared in the "Target type matches" filter
                // (regardless of whether that filter button was active at the time of commit).
                AsyncCompletionLogger.LogCommitWithTargetTypeCompletionExperimentEnabled();
                if (item.Filters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches))
                {
                    AsyncCompletionLogger.LogCommitItemWithTargetTypeFilter();
                }
            }

            // Commit with completion service assumes that null is provided is case of invoke. VS provides '\0' in the case.
            var commitChar = typeChar == '\0' ? null : (char?)typeChar;

            return(Commit(
                       triggerDocument, completionService, session.TextView, subjectBuffer,
                       roslynItem, completionListSpan, commitChar, triggerLocation.Snapshot, serviceRules,
                       filterText, cancellationToken));
        }
Example #22
0
 public ExtendedFilterResult(VSCompletionItem item, FilterResult filterResult)
 {
     VSCompletionItem = item;
     FilterResult     = filterResult;
 }
 public bool ShouldBeFilteredOut(VSCompletionItem item)
 => ShouldBeFilteredOutOfCompletionList(item) || ShouldBeFilteredOutOfExpandedCompletionList(item);