提交 1a8f912f 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #18734 from CyrusNajmabadi/patternMatchCheckAllParts

Check that all parts of a pattern match, even if a client only wants information about the 'first match'.
......@@ -4066,7 +4066,7 @@ public class Test
// TODO...
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/18800")]
[Fact]
public void CS0306ERR_BadTypeArgument01()
{
var source =
......
......@@ -182,6 +182,8 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part
[InlineData("[|Fog|]Bar", "fog", PatternMatchKind.Prefix, CaseInsensitive)]
[InlineData("[|fog|]BarFoo", "Fog", PatternMatchKind.Prefix, CaseInsensitive)]
[InlineData("[|system.ref|]lection", "system.ref", PatternMatchKind.Prefix, CaseSensitive)]
[InlineData("Fog[|B|]ar", "b", PatternMatchKind.Substring, CaseInsensitive)]
[InlineData("_[|my|]Button", "my", PatternMatchKind.Substring, CaseSensitive)]
......@@ -234,10 +236,10 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part
[InlineData("my[|_b|]utton", "_B", PatternMatchKind.CamelCase, CaseInsensitive, PatternMatcher.CamelCaseContiguousBonus)]
[InlineData("[|_|]my_[|b|]utton", "_B", PatternMatchKind.CamelCase, CaseInsensitive, PatternMatcher.CamelCaseMatchesFromStartBonus)]
public void TryMatchSingleWordPattern(
public void TestNonFuzzyMatch(
string candidate, string pattern, int matchKindInt, bool isCaseSensitive, int? camelCaseWeight = null)
{
var match = TryMatchSingleWordPattern(candidate, pattern);
var match = TestNonFuzzyMatch(candidate, pattern);
Assert.NotNull(match);
var matchKind = (PatternMatchKind)matchKindInt;
......@@ -269,9 +271,10 @@ private void VerifyBreakIntoCharacterParts(string original, params string[] part
[InlineData("FogBarBaz", "FZ")]
[InlineData("_mybutton", "myB")]
[InlineData("FogBarChangedEventArgs", "changedeventarrrgh")]
public void TryMatchSingleWordPattern_NoMatch(string candidate, string pattern)
[InlineData("runtime.native.system", "system.reflection")]
public void TestNonFuzzyMatch_NoMatch(string candidate, string pattern)
{
var match = TryMatchSingleWordPattern(candidate, pattern);
var match = TestNonFuzzyMatch(candidate, pattern);
Assert.Null(match);
}
......@@ -470,7 +473,7 @@ public void TryMatchSingleWordPattern_CultureAwareSingleWordPreferCaseSensitiveE
try
{
var match = TryMatchSingleWordPattern("[|ioo|]", "\u0130oo"); // u0130 = Capital I with dot
var match = TestNonFuzzyMatch("[|ioo|]", "\u0130oo"); // u0130 = Capital I with dot
Assert.Equal(PatternMatchKind.Exact, match.Value.Kind);
Assert.False(match.Value.IsCaseSensitive);
......@@ -499,11 +502,15 @@ private static IList<string> BreakIntoCharacterParts(string identifier)
private static IList<string> BreakIntoWordParts(string identifier)
=> PartListToSubstrings(identifier, StringBreaker.BreakIntoWordParts(identifier));
private static PatternMatch? TryMatchSingleWordPattern(string candidate, string pattern)
private static PatternMatch? TestNonFuzzyMatch(string candidate, string pattern)
{
MarkupTestFile.GetSpans(candidate, out candidate, out ImmutableArray<TextSpan> spans);
var match = new PatternMatcher(pattern).MatchSingleWordPattern_ForTestingOnly(candidate);
var match = new PatternMatcher(pattern).GetFirstMatch(candidate, includeMatchSpans: true);
if (match?.Kind == PatternMatchKind.Fuzzy)
{
match = null;
}
if (match == null)
{
......@@ -536,4 +543,4 @@ private static IEnumerable<PatternMatch> TryMatchMultiWordPattern(string candida
}
}
}
}
}
\ No newline at end of file
......@@ -3,6 +3,7 @@
Imports System.Globalization
Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Text
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
' These tests adapted from David Kean's table at
......@@ -10,7 +11,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Public Class CompletionRulesTests
<Fact>
Public Sub TestMatchLowerCaseEnglishI()
Dim wordsToMatch = {"index", "Index", "işte", şte"}
Dim wordsToMatch = {"[|i|]ndex", "[|I|]ndex", "[|i|]şte", "[|İ|]şte"}
Dim wordsToNotMatch = {"ırak"}
TestMatches("i", wordsToMatch)
......@@ -19,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
<Fact>
Public Sub TestMatchDottedUpperTurkishI()
Dim wordsToMatch = {"index", "işte", şte"}
Dim wordsToMatch = {"[|i|]ndex", "[|i|]şte", "[|İ|]şte"}
Dim wordsToNotMatch = {"ırak", "Irak", "Index"}
TestMatches("İ", wordsToMatch)
......@@ -28,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
<Fact>
Public Sub TestMatchNonDottedLowerTurkishI()
Dim wordsToMatch = {"ırak", "Irak"}
Dim wordsToMatch = {"[|ı|]rak", "[|I|]rak"}
Dim wordsToNotMatch = {"index", "işte", "İşte"}
TestMatches("ı", wordsToMatch)
......@@ -37,31 +38,48 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
<Fact>
Public Sub TestMatchEnglishUpperI()
Dim wordsToMatch = {"Index", "index", "ırak", "Irak"}
' In turkish-culture "I" will not match "index". However, we want to verify that
' the underlying completion helper will fallback to doing an en-us check if the
' tr-tr check fails, and that it properly also returns the matched spans in this case.
Dim wordsToMatch = {"[|I|]ndex", "[|i|]ndex", "[|ı|]rak", "[|I|]rak"}
Dim wordsToNotMatch = {"İşte"}
TestMatches("I", wordsToMatch)
TestNotMatches("I", wordsToNotMatch)
End Sub
Private Sub TestMatches(v As String, wordsToMatch() As String)
Private Sub TestMatches(pattern As String, wordsToMatch() As String)
Dim culture = New CultureInfo("tr-TR", useUserOverride:=False)
Dim workspace = New TestWorkspace
Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp)
For Each word In wordsToMatch
For Each wordMarkup In wordsToMatch
Dim word As String = Nothing
Dim wordMatchSpan As TextSpan = Nothing
MarkupTestFile.GetSpan(wordMarkup, word, wordMatchSpan)
Dim item = CompletionItem.Create(word)
Assert.True(helper.MatchesPattern(item.FilterText, v, culture), $"Expected item {word} does not match {v}")
Assert.True(helper.MatchesPattern(item.FilterText, pattern, culture), $"Expected item {word} does not match {pattern}")
Dim highlightedSpans = helper.GetHighlightedSpans(item.FilterText, pattern, culture)
Assert.NotEmpty(highlightedSpans)
Assert.Equal(1, highlightedSpans.Length)
Assert.Equal(wordMatchSpan, highlightedSpans(0))
Next
End Sub
Private Sub TestNotMatches(v As String, wordsToNotMatch() As String)
Private Sub TestNotMatches(pattern As String, wordsToNotMatch() As String)
Dim culture = New CultureInfo("tr-TR", useUserOverride:=False)
Dim workspace = New TestWorkspace
Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp)
For Each word In wordsToNotMatch
Dim item = CompletionItem.Create(word)
Assert.False(helper.MatchesPattern(item.FilterText, v, culture), $"Unexpected item {word} matches {v}")
Assert.False(helper.MatchesPattern(item.FilterText, pattern, culture), $"Unexpected item {word} matches {pattern}")
Dim highlightedSpans = helper.GetHighlightedSpans(item.FilterText, pattern, culture)
Assert.Empty(highlightedSpans)
Next
End Sub
End Class
......
......@@ -112,7 +112,7 @@ public bool MatchesPattern(string text, string pattern, CultureInfo culture)
if (!culture.Equals(EnUSCultureInfo))
{
patternMatcher = this.GetPatternMatcher(pattern, EnUSCultureInfo);
match = patternMatcher.GetFirstMatch(completionItemText);
match = patternMatcher.GetFirstMatch(completionItemText, includeMatchSpans);
if (match != null)
{
return match;
......
......@@ -246,18 +246,27 @@ public PatternMatches GetMatches(string candidate, string dottedContainer)
/// so, unless you need to know the full set of matches, use this version.
/// </remarks>
/// <param name="candidate">The word being tested.</param>
/// <param name="inludeMatchSpans">Whether or not the matched spans should be included with results</param>
/// <param name="includeMatchSpans">Whether or not the matched spans should be included with results</param>
/// <returns>If this was a match, the first element of the set of match types that occurred while matching the
/// patterns. If it was not a match, it returns null.</returns>
public PatternMatch? GetFirstMatch(string candidate, bool inludeMatchSpans = false)
public PatternMatch? GetFirstMatch(
string candidate, bool includeMatchSpans)
{
if (SkipMatch(candidate))
{
return null;
}
return MatchPatternSegment(candidate, inludeMatchSpans, _fullPatternSegment, wantAllMatches: false, allMatches: out _, fuzzyMatch: false) ??
MatchPatternSegment(candidate, inludeMatchSpans, _fullPatternSegment, wantAllMatches: false, allMatches: out _, fuzzyMatch: true);
return GetFirstMatchWorker(candidate, includeMatchSpans, fuzzyMatch: false) ??
GetFirstMatchWorker(candidate, includeMatchSpans, fuzzyMatch: true);
}
private PatternMatch? GetFirstMatchWorker(
string candidate, bool includeMatchSpans, bool fuzzyMatch)
{
return MatchPatternSegment(
candidate, includeMatchSpans, _fullPatternSegment,
wantAllMatches: false, allMatches: out _, fuzzyMatch: fuzzyMatch);
}
private StringBreaks GetWordSpans(string word)
......@@ -268,13 +277,6 @@ private StringBreaks GetWordSpans(string word)
}
}
internal PatternMatch? MatchSingleWordPattern_ForTestingOnly(string candidate)
{
return MatchPatternChunk(candidate, includeMatchSpans: true,
patternChunk: _fullPatternSegment.TotalTextChunk, punctuationStripped: false,
fuzzyMatch: false);
}
private static bool ContainsUpperCaseLetter(string pattern)
{
// Expansion of "foreach(char ch in pattern)" to avoid a CharEnumerator allocation
......@@ -443,12 +445,10 @@ private static bool ContainsSpaceOrAsterisk(string text)
var singleMatch = MatchPatternSegment(candidate, includeMatchSpans, patternSegment,
wantAllMatches: true, fuzzyMatch: fuzzyMatch, allMatches: out var matches);
if (singleMatch.HasValue)
{
return ImmutableArray.Create(singleMatch.Value);
}
return matches;
return singleMatch.HasValue
? ImmutableArray.Create(singleMatch.Value)
: matches;
}
/// <summary>
......@@ -554,17 +554,18 @@ private static bool ContainsSpaceOrAsterisk(string text)
return null;
}
if (!wantAllMatches || subWordTextChunks.Length == 1)
{
// Stop at the first word
return result;
}
matches.Add(result.Value);
}
allMatches = matches.ToImmutable();
return null;
if (wantAllMatches && matches.Count >= 2)
{
allMatches = matches.ToImmutable();
return null;
}
else
{
return matches.FirstOrNullable();
}
}
finally
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册