diff --git a/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynCompletionSet.cs b/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynCompletionSet.cs index d5ac895953c6dbc54c17299a40e526209abaed3d..be3b119b611769ecac75408cfea6df6467327f9a 100644 --- a/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynCompletionSet.cs +++ b/src/EditorFeatures/Core.Wpf/Completion/Presentation/RoslynCompletionSet.cs @@ -1,5 +1,9 @@ -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; +using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Globalization; using System.Linq; using Microsoft.CodeAnalysis.Completion; @@ -29,6 +33,8 @@ internal class RoslynCompletionSet : CompletionSet2 protected Dictionary CompletionItemMap; protected CompletionItem SuggestionModeItem; + private readonly Dictionary _displayTextToBoldingTextMap = new Dictionary(); + protected string FilterText; public RoslynCompletionSet( @@ -90,6 +96,14 @@ public override void Recalculate() { _foregroundThread.AssertIsForeground(); + foreach (var item in completionItems) + { + if (!_displayTextToBoldingTextMap.ContainsKey(item.DisplayText)) + { + _displayTextToBoldingTextMap.Add(item.DisplayText, CompletionHelper.GetDisplayTextForMatching(item)); + } + } + // Initialize the completion map to a reasonable default initial size (+1 for the builder) CompletionItemMap = CompletionItemMap ?? new Dictionary(completionItems.Count + 1); FilterText = filterText; @@ -215,6 +229,9 @@ public override IReadOnlyList GetHighlightedSpansInDisplayText(string disp return null; } + var textForBolding = _displayTextToBoldingTextMap.TryGetValue(displayText, out var matchingText) ? matchingText : displayText; + Debug.Assert(displayText.Contains(textForBolding)); + var pattern = this.FilterText; if (_highlightMatchingPortions && !string.IsNullOrWhiteSpace(pattern)) { @@ -222,9 +239,9 @@ public override IReadOnlyList GetHighlightedSpansInDisplayText(string disp if (completionHelper != null) { var highlightedSpans = completionHelper.GetHighlightedSpans( - displayText, pattern, CultureInfo.CurrentCulture); + textForBolding, pattern, CultureInfo.CurrentCulture); - return highlightedSpans.SelectAsArray(s => s.ToSpan()); + return highlightedSpans.SelectAsArray(s => new Span(s.Start + Math.Max(0, displayText.IndexOf(textForBolding)), s.Length)); } } 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 9f936c90188b1fa3ce7cd8ecf9ab484528b59fa6..34849d5db8171d09683ce97c8880cefc8a9f8001 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -498,7 +498,7 @@ private bool IsBetterDeletionMatch(FilterResult result1, FilterResult result2) private static int GetRecentItemIndex(ImmutableArray recentItems, CompletionItem item) { - var index = recentItems.IndexOf(item.DisplayText); + var index = recentItems.IndexOf(CompletionHelper.GetDisplayTextForMatching(item)); return -index; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs index 9a4764e6f1231b466dc68e169f60c1a0557c78b4..2335f8dc8bd36ddfe6466315efd9a649f4f3c82a 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller_Commit.cs @@ -174,7 +174,7 @@ private CompletionProvider GetCompletionProvider(CompletionItem item) } // Let the completion rules know that this item was committed. - this.MakeMostRecentItem(item.DisplayText); + this.MakeMostRecentItem(CompletionHelper.GetDisplayTextForMatching(item)); } private void SetCaretPosition(int desiredCaretPosition) diff --git a/src/Features/Core/Portable/Completion/CompletionHelper.cs b/src/Features/Core/Portable/Completion/CompletionHelper.cs index 4c5cf8c4b08965088495bdb9d287a706203123f6..24b58678c4952ee4248564037f9b88524c806ab3 100644 --- a/src/Features/Core/Portable/Completion/CompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/CompletionHelper.cs @@ -19,6 +19,11 @@ internal sealed class CompletionHelper private static readonly CultureInfo EnUSCultureInfo = new CultureInfo("en-US"); private readonly bool _isCaseSensitive; + // Support for completion items with extra decorative characters in their DisplayText. + // This allows bolding and MRU to operate on the "real" display text (without text + // decorations). This should be a substring of the corresponding DisplayText. + private static string DisplayTextForMatching = nameof(DisplayTextForMatching); + public CompletionHelper(bool isCaseSensitive) { _isCaseSensitive = isCaseSensitive; @@ -252,5 +257,8 @@ private int ComparePreselection(CompletionItem item1, CompletionItem item2) return 0; } + + internal static string GetDisplayTextForMatching(CompletionItem item) + => item.Properties.TryGetValue(DisplayTextForMatching, out var displayText) ? displayText : item.DisplayText; } }