public static CompletionItem Create( string displayText, Glyph?glyph = null, ImmutableArray <SymbolDisplayPart> description = default(ImmutableArray <SymbolDisplayPart>), string sortText = null, string filterText = null, int?matchPriority = null, bool showsWarningIcon = false, bool shouldFormatOnCommit = false, ImmutableDictionary <string, string> properties = null, ImmutableArray <string> tags = default(ImmutableArray <string>), CompletionItemRules rules = null) { tags = tags.NullToEmpty(); if (glyph != null) { // put glyph tags first tags = GlyphTags.GetTags(glyph.Value).AddRange(tags); } if (showsWarningIcon) { tags = tags.Add(CompletionTags.Warning); } properties = properties ?? ImmutableDictionary <string, string> .Empty; if (!description.IsDefault && description.Length > 0) { properties = properties.Add("Description", EncodeDescription(description)); } rules = rules ?? CompletionItemRules.Default; rules = rules.WithMatchPriority(matchPriority.GetValueOrDefault()) .WithFormatOnCommit(shouldFormatOnCommit); return(CompletionItem.Create( displayText: displayText, filterText: filterText, sortText: sortText, properties: properties, tags: tags, rules: rules)); }
public static CompletionItem Create( string displayText, string displayTextSuffix, CompletionItemRules rules, Glyph?glyph = null, ImmutableArray <SymbolDisplayPart> description = default, string sortText = null, string filterText = null, bool showsWarningIcon = false, ImmutableDictionary <string, string> properties = null, ImmutableArray <string> tags = default, string inlineDescription = null, bool isComplexTextEdit = false) { tags = tags.NullToEmpty(); if (glyph != null) { // put glyph tags first tags = GlyphTags.GetTags(glyph.Value).AddRange(tags); } if (showsWarningIcon) { tags = tags.Add(WellKnownTags.Warning); } properties ??= ImmutableDictionary <string, string> .Empty; if (!description.IsDefault && description.Length > 0) { properties = properties.Add("Description", EncodeDescription(description)); } return(CompletionItem.Create( displayText: displayText, displayTextSuffix: displayTextSuffix, filterText: filterText, sortText: sortText, properties: properties, tags: tags, rules: rules, inlineDescription: inlineDescription, isComplexTextEdit: isComplexTextEdit)); }
private FilteredCompletionModel UpdateCompletionList( IAsyncCompletionSession session, AsyncCompletionSessionDataSnapshot data, CancellationToken cancellationToken) { if (!session.Properties.TryGetProperty(CompletionSource.HasSuggestionItemOptions, out bool hasSuggestedItemOptions)) { // This is the scenario when the session is created out of Roslyn, in some other provider, e.g. in Debugger. // For now, the default hasSuggestedItemOptions is false. hasSuggestedItemOptions = false; } hasSuggestedItemOptions |= data.DisplaySuggestionItem; var filterText = session.ApplicableToSpan.GetText(data.Snapshot); var reason = data.Trigger.Reason; if (!session.Properties.TryGetProperty(CompletionSource.InitialTriggerKind, out CompletionTriggerKind initialRoslynTriggerKind)) { initialRoslynTriggerKind = CompletionTriggerKind.Invoke; } // Check if the user is typing a number. If so, only proceed if it's a number // directly after a <dot>. That's because it is actually reasonable for completion // to be brought up after a <dot> and for the user to want to filter completion // items based on a number that exists in the name of the item. However, when // we are not after a dot (i.e. we're being brought up after <space> is typed) // then we don't want to filter things. Consider the user writing: // // dim i =<space> // // We'll bring up the completion list here (as VB has completion on <space>). // If the user then types '3', we don't want to match against Int32. if (filterText.Length > 0 && char.IsNumber(filterText[0])) { if (!IsAfterDot(data.Snapshot, session.ApplicableToSpan)) { // Dismiss the session. return(null); } } // We need to filter if a non-empty strict subset of filters are selected var selectedFilters = data.SelectedFilters.Where(f => f.IsSelected).Select(f => f.Filter).ToImmutableArray(); var needToFilter = selectedFilters.Length > 0 && selectedFilters.Length < data.SelectedFilters.Length; var filterReason = Helpers.GetFilterReason(data.Trigger); // If the session was created/maintained out of Roslyn, e.g. in debugger; no properties are set and we should use data.Snapshot. // However, we prefer using the original snapshot in some projection scenarios. if (!session.Properties.TryGetProperty(CompletionSource.TriggerSnapshot, out ITextSnapshot snapshotForDocument)) { snapshotForDocument = data.Snapshot; } var document = snapshotForDocument.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); var completionService = document?.GetLanguageService <CompletionService>(); var completionRules = completionService?.GetRules() ?? CompletionRules.Default; var completionHelper = document != null?CompletionHelper.GetHelper(document) : _defaultCompletionHelper; var initialListOfItemsToBeIncluded = new List <ExtendedFilterResult>(); foreach (var item in data.InitialSortedList) { cancellationToken.ThrowIfCancellationRequested(); if (needToFilter && ShouldBeFilteredOutOfCompletionList(item, selectedFilters)) { continue; } if (!item.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem)) { roslynItem = RoslynCompletionItem.Create( displayText: item.DisplayText, filterText: item.FilterText, sortText: item.SortText, displayTextSuffix: item.Suffix); } if (MatchesFilterText(completionHelper, roslynItem, filterText, initialRoslynTriggerKind, filterReason, _recentItemsManager.RecentItems)) { initialListOfItemsToBeIncluded.Add(new ExtendedFilterResult(item, new FilterResult(roslynItem, filterText, matchedFilterText: true))); } else { // The item didn't match the filter text. We'll still keep it in the list // if one of two things is true: // // 1. The user has only typed a single character. In this case they might // have just typed the character to get completion. Filtering out items // here is not desirable. // // 2. They brough up completion with ctrl-j or through deletion. In these // cases we just always keep all the items in the list. if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion || initialRoslynTriggerKind == CompletionTriggerKind.Invoke || filterText.Length <= 1) { initialListOfItemsToBeIncluded.Add(new ExtendedFilterResult(item, new FilterResult(roslynItem, filterText, matchedFilterText: false))); } } } if (data.Trigger.Reason == CompletionTriggerReason.Backspace && completionRules.DismissIfLastCharacterDeleted && session.ApplicableToSpan.GetText(data.Snapshot).Length == 0) { // Dismiss the session return(null); } if (initialListOfItemsToBeIncluded.Count == 0) { return(HandleAllItemsFilteredOut(reason, data.SelectedFilters, completionRules)); } var options = document?.Project.Solution.Options; var highlightMatchingPortions = options?.GetOption(CompletionOptions.HighlightMatchingPortionsOfCompletionListItems, document.Project.Language) ?? true; var showCompletionItemFilters = options?.GetOption(CompletionOptions.ShowCompletionItemFilters, document.Project.Language) ?? true; var updatedFilters = showCompletionItemFilters ? GetUpdatedFilters(initialListOfItemsToBeIncluded, data.SelectedFilters) : ImmutableArray <CompletionFilterWithState> .Empty; var highlightedList = GetHighlightedList(initialListOfItemsToBeIncluded, filterText, completionHelper, highlightMatchingPortions).ToImmutableArray(); // If this was deletion, then we control the entire behavior of deletion ourselves. if (initialRoslynTriggerKind == CompletionTriggerKind.Deletion) { return(HandleDeletionTrigger(data.Trigger.Reason, initialListOfItemsToBeIncluded, filterText, updatedFilters, highlightedList)); } Func <ImmutableArray <RoslynCompletionItem>, string, ImmutableArray <RoslynCompletionItem> > filterMethod; if (completionService == null) { filterMethod = (items, text) => CompletionService.FilterItems(completionHelper, items, text); } else { filterMethod = (items, text) => completionService.FilterItems(document, items, text); } return(HandleNormalFiltering( filterMethod, filterText, updatedFilters, initialRoslynTriggerKind, filterReason, data.Trigger.Character, initialListOfItemsToBeIncluded, highlightedList, completionHelper, hasSuggestedItemOptions)); }