diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 04c472d014f93c6dfe6d7ad33cad7bc743012492..00425ea6f6dc73fcab5b1e912484e8126b067333 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -480,7 +480,11 @@ private bool IsBetterDeletionMatch(FilterResult result1, FilterResult result2) } } - return helper.MatchesFilterText(item, filterText, CultureInfo.CurrentCulture); + // Checks if the given completion item matches the pattern provided so far. + // A completion item is checked against the pattern by see if it's + // CompletionItem.FilterText matches the item. That way, the pattern it checked + // against terms like "IList" and not IList<> + return helper.MatchesPattern(item.FilterText, filterText, CultureInfo.CurrentCulture); } private static int GetRecentItemIndex(ImmutableArray recentItems, CompletionItem item) diff --git a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs index 9e834f14dfcb1117cabb3b7ba6b9ed1799c6eaf9..ade78bfb6f81654e5512b295f231bb9f6c967d3e 100644 --- a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs +++ b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/Roslyn15CompletionSet.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; @@ -71,22 +73,22 @@ private CompletionHelper GetCompletionHelper() public IReadOnlyList GetHighlightedSpansInDisplayText(string displayText) { - if (_highlightMatchingPortions && !string.IsNullOrWhiteSpace(FilterText)) + if (SuggestionModeItem != null && SuggestionModeItem.DisplayText == displayText) + { + // Don't highlight the builder-completion-item. + return null; + } + + var pattern = this.FilterText; + if (_highlightMatchingPortions && !string.IsNullOrWhiteSpace(pattern)) { var completionHelper = this.GetCompletionHelper(); if (completionHelper != null) { - var completionItem = this.CompletionItemMap.Keys.FirstOrDefault(k => k.DisplayText == displayText); + var highlightedSpans = completionHelper.GetHighlightedSpans( + displayText, pattern, CultureInfo.CurrentCulture); - if (completionItem != null && completionItem != SuggestionModeItem) - { - var highlightedSpans = completionHelper.GetHighlightedSpans( - completionItem, FilterText, CultureInfo.CurrentCulture); - if (highlightedSpans != null) - { - return highlightedSpans.Select(s => s.ToSpan()).ToArray(); - } - } + return highlightedSpans.SelectAsArray(s => s.ToSpan()); } } diff --git a/src/Features/Core/Portable/Completion/CompletionHelper.cs b/src/Features/Core/Portable/Completion/CompletionHelper.cs index 007c0ee0964890fad933c47c5cb8394da3f8fa98..f7807d030d9abd0cd46af6cbf72e29fed59b14af 100644 --- a/src/Features/Core/Portable/Completion/CompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/CompletionHelper.cs @@ -6,7 +6,6 @@ using System.Globalization; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PatternMatching; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Completion @@ -49,42 +48,39 @@ public static CompletionHelper GetHelper(Document document) } public ImmutableArray GetHighlightedSpans( - CompletionItem completionItem, string filterText, CultureInfo culture) + string text, string pattern, CultureInfo culture) { - var match = GetMatch(completionItem, filterText, includeMatchSpans: true, culture: culture); + // Use the display text of the completion item + var match = GetMatch(text, pattern, includeMatchSpans: true, culture: culture); return match == null ? ImmutableArray.Empty : match.Value.MatchedSpans; } /// - /// Returns true if the completion item matches the filter text typed so far. Returns 'true' + /// Returns true if the completion item matches the pattern so far. Returns 'true' /// iff the completion item matches and should be included in the filtered completion /// results, or false if it should not be. /// - public bool MatchesFilterText(CompletionItem item, string filterText, CultureInfo culture) - { - return GetMatch(item, filterText, culture) != null; - } + public bool MatchesPattern(string text, string pattern, CultureInfo culture) + => GetMatch(text, pattern, culture) != null; - private PatternMatch? GetMatch(CompletionItem item, string filterText, CultureInfo culture) - { - return GetMatch(item, filterText, includeMatchSpans: false, culture: culture); - } + private PatternMatch? GetMatch(string text, string pattern, CultureInfo culture) + => GetMatch(text, pattern, includeMatchSpans: false, culture: culture); private PatternMatch? GetMatch( - CompletionItem item, string filterText, + string completionItemText, string pattern, bool includeMatchSpans, CultureInfo culture) { // If the item has a dot in it (i.e. for something like enum completion), then attempt // to match what the user wrote against the last portion of the name. That way if they - // write "Bl" and we have "Blub" and "Color.Black", we'll consider hte latter to be a + // write "Bl" and we have "Blub" and "Color.Black", we'll consider the latter to be a // better match as they'll both be prefix matches, and the latter will have a higher // priority. - var lastDotIndex = item.FilterText.LastIndexOf('.'); + var lastDotIndex = completionItemText.LastIndexOf('.'); if (lastDotIndex >= 0) { - var textAfterLastDot = item.FilterText.Substring(lastDotIndex + 1); - var match = GetMatchWorker(textAfterLastDot, filterText, includeMatchSpans, culture); + var textAfterLastDot = completionItemText.Substring(lastDotIndex + 1); + var match = GetMatchWorker(textAfterLastDot, pattern, includeMatchSpans, culture); if (match != null) { return match; @@ -93,14 +89,14 @@ public bool MatchesFilterText(CompletionItem item, string filterText, CultureInf // Didn't have a dot, or the user text didn't match the portion after the dot. // Just do a normal check against the entire completion item. - return GetMatchWorker(item.FilterText, filterText, includeMatchSpans, culture); + return GetMatchWorker(completionItemText, pattern, includeMatchSpans, culture); } private PatternMatch? GetMatchWorker( - string completionItemText, string filterText, + string completionItemText, string pattern, bool includeMatchSpans, CultureInfo culture) { - var patternMatcher = this.GetPatternMatcher(filterText, culture); + var patternMatcher = this.GetPatternMatcher(pattern, culture); var match = patternMatcher.GetFirstMatch(completionItemText, includeMatchSpans); if (match != null) @@ -111,7 +107,7 @@ public bool MatchesFilterText(CompletionItem item, string filterText, CultureInf // Start with the culture-specific comparison, and fall back to en-US. if (!culture.Equals(EnUSCultureInfo)) { - patternMatcher = this.GetEnUSPatternMatcher(filterText); + patternMatcher = this.GetEnUSPatternMatcher(pattern); match = patternMatcher.GetFirstMatch(completionItemText); if (match != null) { @@ -123,7 +119,7 @@ public bool MatchesFilterText(CompletionItem item, string filterText, CultureInf } private PatternMatcher GetPatternMatcher( - string value, CultureInfo culture, Dictionary> map) + string pattern, CultureInfo culture, Dictionary> map) { lock (_gate) { @@ -133,36 +129,32 @@ public bool MatchesFilterText(CompletionItem item, string filterText, CultureInf map[culture] = innerMap; } - if (!innerMap.TryGetValue(value, out var patternMatcher)) + if (!innerMap.TryGetValue(pattern, out var patternMatcher)) { - patternMatcher = new PatternMatcher(value, culture, + patternMatcher = new PatternMatcher(pattern, culture, verbatimIdentifierPrefixIsWordCharacter: true, allowFuzzyMatching: false); - innerMap.Add(value, patternMatcher); + innerMap.Add(pattern, patternMatcher); } return patternMatcher; } } - private PatternMatcher GetPatternMatcher(string value, CultureInfo culture) - { - return GetPatternMatcher(value, culture, _patternMatcherMap); - } + private PatternMatcher GetPatternMatcher(string pattern, CultureInfo culture) + => GetPatternMatcher(pattern, culture, _patternMatcherMap); - private PatternMatcher GetEnUSPatternMatcher(string value) - { - return GetPatternMatcher(value, EnUSCultureInfo, _fallbackPatternMatcherMap); - } + private PatternMatcher GetEnUSPatternMatcher(string pattern) + => GetPatternMatcher(pattern, EnUSCultureInfo, _fallbackPatternMatcherMap); /// /// Returns true if item1 is a better completion item than item2 given the provided filter /// text, or false if it is not better. /// - public int CompareItems(CompletionItem item1, CompletionItem item2, string filterText, CultureInfo culture) + public int CompareItems(CompletionItem item1, CompletionItem item2, string pattern, CultureInfo culture) { - var match1 = GetMatch(item1, filterText, culture); - var match2 = GetMatch(item2, filterText, culture); + var match1 = GetMatch(item1.FilterText, pattern, culture); + var match2 = GetMatch(item2.FilterText, pattern, culture); if (match1 != null && match2 != null) {