Skip to content

Commit 6df5a50

Browse files
Cyrus NajmabadiCyrus Najmabadi
authored andcommitted
Deal with VB verbatim strings
1 parent 31bc7ef commit 6df5a50

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed

src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/NavigateToSearchIndex.NavigateToSearchInfo.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,14 @@ public PatternMatcherKind ProbablyContainsMatch(string patternName, string? patt
335335
/// </summary>
336336
private bool NonFuzzyCheckPasses(ReadOnlySpan<char> pattern)
337337
{
338-
// Strip leading non-letter/digit characters upfront (e.g., "@static" → "static")
339-
// to avoid a wasted hump/trigram pass on the raw pattern when it has a junk prefix.
340-
while (pattern.Length > 0 && !char.IsLetterOrDigit(pattern[0]))
338+
// Strip leading and trailing non-word characters (e.g., "@static" → "static",
339+
// "[class]" → "class") to match how PatternMatcher.PatternSegment extracts sub-words.
340+
while (pattern.Length > 0 && !PatternMatcher.IsWordChar(pattern[0]))
341341
pattern = pattern[1..];
342342

343+
while (pattern.Length > 0 && !PatternMatcher.IsWordChar(pattern[^1]))
344+
pattern = pattern[..^1];
345+
343346
if (pattern.Length == 0)
344347
return false;
345348

src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ private bool MatchPatternSegment(
386386
}
387387
}
388388

389-
private static bool IsWordChar(char ch)
389+
internal static bool IsWordChar(char ch)
390390
=> char.IsLetterOrDigit(ch) || ch == '_';
391391

392392
/// <summary>

src/Workspaces/CoreTest/FindSymbols/NavigateToSearchIndexTests.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,5 +1216,81 @@ public void EndToEnd_OnlyFuzzyChecksPasses_FuzzyMatchUsed()
12161216
Assert.Equal(PatternMatchKind.Fuzzy, match.Value.Kind);
12171217
}
12181218

1219+
/// <summary>
1220+
/// Mirrors the VB NavigateTo test "TestFindVerbatimClass": searching for "class" (all-lowercase,
1221+
/// length 5) against a symbol named "Class" (length 5) must produce an Exact match, not Fuzzy.
1222+
/// The non-fuzzy pass in the PatternMatcher should find this as a case-insensitive exact match
1223+
/// before the fuzzy pass is even attempted.
1224+
/// </summary>
1225+
[Fact]
1226+
public void EndToEnd_CaseInsensitiveExact_Length5_ProducesExactNotFuzzy()
1227+
{
1228+
var index = CreateIndex(("Class", ""));
1229+
1230+
var matchKinds = index.CouldContainNavigateToMatch("class", null);
1231+
Assert.True(matchKinds.HasFlag(PatternMatcherKind.Standard));
1232+
1233+
using var matcher = PatternMatcher.CreatePatternMatcher("class", includeMatchedSpans: false, matchKinds);
1234+
var match = matcher.GetFirstMatch("Class");
1235+
Assert.NotNull(match);
1236+
Assert.Equal(PatternMatchKind.Exact, match.Value.Kind);
1237+
}
1238+
1239+
/// <summary>
1240+
/// Mirrors the VB NavigateTo test "TestFindVerbatimClass" for the "[class]" search.
1241+
/// In VB, brackets are used to escape keywords as identifiers. The pattern "[class]" should
1242+
/// strip brackets as punctuation, leaving "class" which matches "Class" as Exact.
1243+
/// </summary>
1244+
[Fact]
1245+
public void EndToEnd_BracketedPattern_ProducesExactNotFuzzy()
1246+
{
1247+
var index = CreateIndex(("Class", ""));
1248+
1249+
var matchKinds = index.CouldContainNavigateToMatch("[class]", null);
1250+
1251+
Assert.True(matchKinds.HasFlag(PatternMatcherKind.Standard), $"Expected Standard flag but got: {matchKinds}");
1252+
1253+
using var matcher = PatternMatcher.CreatePatternMatcher("[class]", includeMatchedSpans: false, matchKinds);
1254+
var match = matcher.GetFirstMatch("Class");
1255+
Assert.NotNull(match);
1256+
Assert.Equal(PatternMatchKind.Exact, match.Value.Kind);
1257+
}
1258+
1259+
/// <summary>
1260+
/// Underscores are valid word characters and must NOT be stripped by the pre-filter.
1261+
/// A pattern like "_myField" should match symbols with that name via standard (non-fuzzy) matching.
1262+
/// </summary>
1263+
[Fact]
1264+
public void EndToEnd_UnderscorePattern_PreservesUnderscore()
1265+
{
1266+
var index = CreateIndex(("_myField", ""));
1267+
1268+
var matchKinds = index.CouldContainNavigateToMatch("_myField", null);
1269+
Assert.True(matchKinds.HasFlag(PatternMatcherKind.Standard), $"Expected Standard flag but got: {matchKinds}");
1270+
1271+
using var matcher = PatternMatcher.CreatePatternMatcher("_myField", includeMatchedSpans: false, matchKinds);
1272+
var match = matcher.GetFirstMatch("_myField");
1273+
Assert.NotNull(match);
1274+
Assert.Equal(PatternMatchKind.Exact, match.Value.Kind);
1275+
}
1276+
1277+
/// <summary>
1278+
/// Underscores surrounded by brackets (e.g., "[_class]" in VB) should strip the brackets
1279+
/// but preserve the underscore, matching "_class" as Exact.
1280+
/// </summary>
1281+
[Fact]
1282+
public void EndToEnd_BracketedUnderscorePattern_PreservesUnderscore()
1283+
{
1284+
var index = CreateIndex(("_class", ""));
1285+
1286+
var matchKinds = index.CouldContainNavigateToMatch("[_class]", null);
1287+
Assert.True(matchKinds.HasFlag(PatternMatcherKind.Standard), $"Expected Standard flag but got: {matchKinds}");
1288+
1289+
using var matcher = PatternMatcher.CreatePatternMatcher("[_class]", includeMatchedSpans: false, matchKinds);
1290+
var match = matcher.GetFirstMatch("_class");
1291+
Assert.NotNull(match);
1292+
Assert.Equal(PatternMatchKind.Exact, match.Value.Kind);
1293+
}
1294+
12191295
#endregion
12201296
}

0 commit comments

Comments
 (0)