From 08b7e5e659a0ce9ba8843a28f0c8cc6026446ec1 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 22 Sep 2026 19:24:09 +0000 Subject: [PATCH] Match required glob tokens by any word under ByWordAll DoesMatchGlob picked AllTokensMatchGlobFilter for required tokens whenever the match option was not ByWordAny, so a "+world" token asked whether *every* word in the text matched the glob "world" rather than whether "world" appeared among them. Any multi-word text failed, making +token filters unusable under ByWordAll. Required tokens now always use AnyTokenMatchesGlobFilter, matching the excluded-token branch a few lines above, which asks the same question and never varied by match option. Fixes ktsu-dev/TextFilter#95 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UscjStBJdW3uHm5NR289DX --- TextFilter.Test/TextFilterTests.cs | 21 +++++++++++++++++++++ TextFilter/TextFilter.cs | 10 +++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/TextFilter.Test/TextFilterTests.cs b/TextFilter.Test/TextFilterTests.cs index e74ca60..a892f07 100644 --- a/TextFilter.Test/TextFilterTests.cs +++ b/TextFilter.Test/TextFilterTests.cs @@ -521,6 +521,27 @@ public void DoesMatchGlobWithByWordAllOptionReturnsFalse() Assert.IsFalse(result, "Not all words matching with ByWordAll option should return false."); } + [TestMethod] + public void DoesMatchGlobWithRequiredTokenByWordAllReturnsTrue() + { + bool result = TextFilter.DoesMatchGlob("hello world", "hello* +world", TextFilterMatchOptions.ByWordAll); + Assert.IsTrue(result, "A required token present among the text's words should return true under ByWordAll."); + } + + [TestMethod] + public void DoesMatchGlobWithAllRequiredTokensByWordAllReturnsTrue() + { + bool result = TextFilter.DoesMatchGlob("hello world", "+hello +world", TextFilterMatchOptions.ByWordAll); + Assert.IsTrue(result, "Every required token being present should return true under ByWordAll."); + } + + [TestMethod] + public void DoesMatchGlobWithMissingRequiredTokenByWordAllReturnsFalse() + { + bool result = TextFilter.DoesMatchGlob("hello world", "hello* +missing", TextFilterMatchOptions.ByWordAll); + Assert.IsFalse(result, "A required token absent from the text should still return false under ByWordAll."); + } + [TestMethod] public void DoesMatchGlobHandlesPartialFilter() { diff --git a/TextFilter/TextFilter.cs b/TextFilter/TextFilter.cs index 408752a..8b0c24a 100644 --- a/TextFilter/TextFilter.cs +++ b/TextFilter/TextFilter.cs @@ -283,11 +283,11 @@ public static bool DoesMatchGlob(string text, string filter, TextFilterMatchOpti return false; // optional tokens were set but text does not contain any optional tokens } - Func, bool> requiredMatchFunc = textFilterMatchOptions is TextFilterMatchOptions.ByWordAny - ? AnyTokenMatchesGlobFilter - : AllTokensMatchGlobFilter; - - bool allRequiredMatches = requiredTokens.All(filterToken => requiredMatchFunc(filterToken, textTokens)); + // A required token asks whether it appears among the text's words, which is the same + // question the excluded tokens above ask, so it uses the same function under every match + // option. Matching it with AllTokensMatchGlobFilter under ByWordAll would instead demand + // that every word in the text match the one required token, which no multi-word text can do. + bool allRequiredMatches = requiredTokens.All(filterToken => AnyTokenMatchesGlobFilter(filterToken, textTokens)); if (!allRequiredMatches) {