diff --git a/TextFilter.Test/ReadmeTests.cs b/TextFilter.Test/ReadmeTests.cs index 7ced909..17fc9c2 100644 --- a/TextFilter.Test/ReadmeTests.cs +++ b/TextFilter.Test/ReadmeTests.cs @@ -4,6 +4,7 @@ namespace TextFilter.Test; using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; @@ -89,7 +90,10 @@ public void EveryMethodInTheReadmeApiReferenceExistsWithItsDocumentedDefaults() foreach (Match row in rows) { string methodName = row.Groups["method"].Value; - MethodInfo[] overloads = [.. methods.Where(m => m.Name == methodName)]; + // The binary-compatibility overloads are hidden from IntelliSense and deliberately carry no + // defaults, since defaults on both sets would make calls ambiguous. The README documents the + // visible overloads, so only those are held to its defaults. + MethodInfo[] overloads = [.. methods.Where(m => m.Name == methodName && !IsHiddenFromCallers(m))]; if (overloads.Length == 0) { problems.Add($"{methodName} does not exist"); @@ -115,6 +119,9 @@ public void EveryMethodInTheReadmeApiReferenceExistsWithItsDocumentedDefaults() Assert.IsEmpty(problems, string.Join("; ", problems)); } + private static bool IsHiddenFromCallers(MethodInfo method) => + method.GetCustomAttribute()?.State == EditorBrowsableState.Never; + [TestMethod] public void ReadmeMatchOptionsSampleGivesTheDocumentedResults() { diff --git a/TextFilter.Test/TextFilterTests.cs b/TextFilter.Test/TextFilterTests.cs index 886b9cd..7d900d8 100644 --- a/TextFilter.Test/TextFilterTests.cs +++ b/TextFilter.Test/TextFilterTests.cs @@ -1303,6 +1303,15 @@ public void GlobStarJoinedToLiteralStillMatchesWithSeparator(string text, string Assert.IsTrue(TextFilter.IsMatch(text, filter, TextFilterType.Glob, TextFilterMatchOptions.ByWholeString)); } + [TestMethod] + [DataRow("a/b/c/d/x.cs", "a**/b**/c**/d**/**/x.cs")] + [DataRow("a/b/c/d/q/x.cs", "a**/b**/c**/d**/**/x.cs")] + [DataRow("a/b/c/d/e/x.cs", "a**/b**/c**/d**/e**/**/x.cs")] + public void GlobStarJoinedToLiteralDoesNotUseUpTheZeroSegmentCap(string text, string filter) + { + Assert.IsTrue(TextFilter.IsMatch(text, filter, TextFilterType.Glob, TextFilterMatchOptions.ByWholeString)); + } + [TestMethod] public void GlobStarSlashFiltersPathItems() { diff --git a/TextFilter/TextFilter.cs b/TextFilter/TextFilter.cs index 16701d8..bfdf03b 100644 --- a/TextFilter/TextFilter.cs +++ b/TextFilter/TextFilter.cs @@ -471,6 +471,7 @@ internal static IReadOnlyList ExpandGlobStarSegments(string maskedToken) { string literal = maskedToken[start..index]; bool expandBothWays = expanded < MaxExpandedGlobStarSegments; + bool doubled = false; List next = new(expansions.Count * 2); foreach (string prefix in expansions) @@ -483,11 +484,18 @@ internal static IReadOnlyList ExpandGlobStarSegments(string maskedToken) if (expandBothWays && (before.Length == 0 || before[^1] == MaskedPathSeparator)) { next.Add(before); + doubled = true; } } + // Only a "**/" that actually added a zero-segment form counts against the cap, so ones + // joined to a literal don't use it up before a later whole-segment "**/" is reached. expansions = next; - expanded++; + if (doubled) + { + expanded++; + } + start = index + MaskedGlobStarSegment.Length; }