提交 09551d64 编写于 作者: I Ivan Basov

UniqueItem is not triggered when completion is initiated by backspace

上级 c20bacb4

// 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 Roslyn.Utilities;
using Microsoft.CodeAnalysis.Completion;
namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion
......@@ -17,5 +18,51 @@ public FilterResult(CompletionItem completionItem, string filterText, bool match
MatchedFilterText = matchedFilterText;
FilterText = filterText;
}
public static int Compare(FilterResult result1, FilterResult result2)
{
var item1 = result1.CompletionItem;
var item2 = result2.CompletionItem;
var prefixLength1 = item1.FilterText.GetCaseInsensitivePrefixLength(result1.FilterText);
var prefixLength2 = item2.FilterText.GetCaseInsensitivePrefixLength(result2.FilterText);
// Prefer the item that matches a longer prefix of the filter text.
if (prefixLength1 != prefixLength2)
{
return prefixLength1 > prefixLength2 ? 1 : -1;
}
else
{
// If the lengths are the same, prefer the one with the higher match priority.
// But only if it's an item that would have been hard selected. We don't want
// to aggressively select an item that was only going to be softly offered.
var item1Priority = item1.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection
? item1.Rules.MatchPriority : MatchPriority.Default;
var item2Priority = item2.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection
? item2.Rules.MatchPriority : MatchPriority.Default;
if (item1Priority != item2Priority)
{
return item1Priority > item2Priority ? 1 : -1;
}
prefixLength1 = item1.FilterText.GetCaseSensitivePrefixLength(result1.FilterText);
prefixLength2 = item2.FilterText.GetCaseSensitivePrefixLength(result2.FilterText);
// If there are "Abc" vs "abc", we should prefer the case typed by user.
if (prefixLength1 != prefixLength2)
{
return prefixLength1 > prefixLength2 ? 1 : -1;
}
if (result1.CompletionItem.IsPreferredItem() != result2.CompletionItem.IsPreferredItem())
{
return result1.CompletionItem.IsPreferredItem() ? 1 : -1;
}
return 0;
}
}
}
}
......@@ -9,8 +9,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
......@@ -357,14 +355,27 @@ private static bool IsAfterDot(ITextSnapshot snapshot, ITrackingSpan applicableT
}
ExtendedFilterResult? bestFilterResult = null;
bool moreThanOneMatchWithSamePriority = false;
foreach (var currentFilterResult in matchingItems)
{
if (bestFilterResult == null ||
IsBetterDeletionMatch(currentFilterResult.FilterResult, bestFilterResult.Value.FilterResult))
if (bestFilterResult == null)
{
// We had no best result yet, so this is now our best result.
bestFilterResult = currentFilterResult;
}
else
{
var match = FilterResult.Compare(currentFilterResult.FilterResult, bestFilterResult.Value.FilterResult);
if (match > 0)
{
moreThanOneMatchWithSamePriority = false;
bestFilterResult = currentFilterResult;
}
else if (match == 0)
{
moreThanOneMatchWithSamePriority = true;
}
}
}
int index;
......@@ -391,13 +402,11 @@ private static bool IsAfterDot(ITextSnapshot snapshot, ITrackingSpan applicableT
hardSelect = false;
}
var deduplicatedListCount = matchingItems.Where(r => !r.VSCompletionItem.IsPreferredItem()).Count();
return new FilteredCompletionModel(
highlightedList, index, filters,
hardSelect ? UpdateSelectionHint.Selected : UpdateSelectionHint.SoftSelected,
centerSelection: true,
uniqueItem: deduplicatedListCount == 1 ? bestFilterResult.GetValueOrDefault().VSCompletionItem : default);
uniqueItem: moreThanOneMatchWithSamePriority ? default : bestFilterResult.GetValueOrDefault().VSCompletionItem);
}
private FilteredCompletionModel HandleAllItemsFilteredOut(
......@@ -531,51 +540,7 @@ internal static int GetRecentItemIndex(ImmutableArray<string> recentItems, Rosly
}
internal static bool IsBetterDeletionMatch(FilterResult result1, FilterResult result2)
{
var item1 = result1.CompletionItem;
var item2 = result2.CompletionItem;
var prefixLength1 = item1.FilterText.GetCaseInsensitivePrefixLength(result1.FilterText);
var prefixLength2 = item2.FilterText.GetCaseInsensitivePrefixLength(result2.FilterText);
// Prefer the item that matches a longer prefix of the filter text.
if (prefixLength1 > prefixLength2)
{
return true;
}
if (prefixLength1 == prefixLength2)
{
// If the lengths are the same, prefer the one with the higher match priority.
// But only if it's an item that would have been hard selected. We don't want
// to aggressively select an item that was only going to be softly offered.
var item1Priority = item1.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection
? item1.Rules.MatchPriority : MatchPriority.Default;
var item2Priority = item2.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection
? item2.Rules.MatchPriority : MatchPriority.Default;
if (item1Priority > item2Priority)
{
return true;
}
prefixLength1 = item1.FilterText.GetCaseSensitivePrefixLength(result1.FilterText);
prefixLength2 = item2.FilterText.GetCaseSensitivePrefixLength(result2.FilterText);
// If there are "Abc" vs "abc", we should prefer the case typed by user.
if (prefixLength1 > prefixLength2)
{
return true;
}
if (result1.CompletionItem.IsPreferredItem() && !result2.CompletionItem.IsPreferredItem())
{
return true;
}
}
return false;
}
=> FilterResult.Compare(result1, result2) == 1;
internal static bool MatchesFilterText(
CompletionHelper helper, RoslynCompletionItem item,
......
......@@ -4968,7 +4968,7 @@ class C
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestSendCommitIfUniqueInDeletionSession(completionImplementation As CompletionImplementation) As Task
Public Async Function TestSendCommitIfUniqueInDeletionSession1(completionImplementation As CompletionImplementation) As Task
' We explicitly use a weak matching on Delete.
' It matches by the first letter. Therefore, if backspace in s.Length, it matches s.Length and s.LastIndexOf.
' In this case, CommitIfUnique is not applied.
......@@ -4995,6 +4995,32 @@ class C
End Using
End Function
<MemberData(NameOf(AllCompletionImplementations))>
<WpfTheory, Trait(Traits.Feature, Traits.Features.Completion)>
Public Async Function TestSendCommitIfUniqueInDeletionSession2(completionImplementation As CompletionImplementation) As Task
Using state = TestStateFactory.CreateCSharpTestState(completionImplementation,
<Document>
using System;
class C
{
void Method()
{
AccessViolationException$$
}
}
</Document>)
state.Workspace.Options = state.Workspace.Options.WithChangedOption(
CompletionOptions.TriggerOnDeletion, LanguageNames.CSharp, True)
state.SendBackspace()
Await state.AssertCompletionSession()
state.SendCommitUniqueCompletionListItem()
Await state.AssertNoCompletionSession()
Assert.Contains("AccessViolationException", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal)
End Using
End Function
' Implementation for the Modern completion only
<InlineData(CompletionImplementation.Modern)>
<WpfTheory, Trait(Traits.Feature, Traits.Features.Completion)>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册