From f3c9dd7f3b1d05aa79202946c288d554e5f45b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sim=C3=A3o=20Caixas?= Date: Mon, 23 Mar 2026 07:12:46 +0000 Subject: [PATCH] Fix song select search not matching results when punctuation marks surround the searched double-quoted phrase (#37034) Fixes #36645 The IsolatedPhrase match mode in FilterCriteria used \s (whitespace) as word boundaries, meaning a search term had to be surrounded by spaces (or start/end of string) to match. This caused songs with titles like 'Music Theme "Some Artist"' to not appear when searching for "Some Artist", since the phrase was bounded by quote characters rather than spaces. This change extends the boundary pattern to also accept Unicode punctuation (\p{P}) alongside whitespace, so terms surrounded by quotes, commas are correctly matched as isolated phrases. Tests have been added to cover searching for terms adjacent to punctuation boundaries (TestCriteriaMatchingTermsAdjacentToPunctuation). Tests cover the example mentioned above. Code inspector with no errors and tests passing. --- .../NonVisual/Filtering/FilterMatchingTest.cs | 40 +++++++++++++++++++ osu.Game/Screens/Select/FilterCriteria.cs | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs index eb75f75dee..345076652a 100644 --- a/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs +++ b/osu.Game.Tests/NonVisual/Filtering/FilterMatchingTest.cs @@ -219,6 +219,46 @@ namespace osu.Game.Tests.NonVisual.Filtering ClassicAssert.AreEqual(filtered, carouselItem.Filtered.Value); } + [Test] + [TestCase("\"quoted words\"", false)] + [TestCase("\"the artist\"", false)] + [TestCase("the artist \"quoted words\"", false)] + [TestCase("\"unknown\"", true)] + public void TestCriteriaMatchingTermsAdjacentToPunctuation(string terms, bool filtered) + { + var exampleBeatmapInfo = getExampleBeatmap(); + exampleBeatmapInfo.Metadata.Title = "the artist \"quoted words\""; + var criteria = new FilterCriteria + { + Ruleset = new RulesetInfo { OnlineID = 6 }, + AllowConvertedBeatmaps = true, + SearchText = terms + }; + var carouselItem = new CarouselBeatmap(exampleBeatmapInfo); + carouselItem.Filter(criteria); + ClassicAssert.AreEqual(filtered, carouselItem.Filtered.Value); + } + + [Test] + [TestCase("~quoted words~", false)] + [TestCase("the artist", false)] + [TestCase("the artist ~quoted words~", false)] + [TestCase("~unknown~", true)] + public void TestCriteriaMatchingTermsAdjacentToMathSymbols(string terms, bool filtered) + { + var exampleBeatmapInfo = getExampleBeatmap(); + exampleBeatmapInfo.Metadata.Title = "the artist ~quoted words~"; + var criteria = new FilterCriteria + { + Ruleset = new RulesetInfo { OnlineID = 6 }, + AllowConvertedBeatmaps = true, + SearchText = terms + }; + var carouselItem = new CarouselBeatmap(exampleBeatmapInfo); + carouselItem.Filter(criteria); + ClassicAssert.AreEqual(filtered, carouselItem.Filtered.Value); + } + [Test] [TestCase("", false)] [TestCase("Goes", false)] diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 485c4d1d72..1c70132850 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -219,7 +219,7 @@ namespace osu.Game.Screens.Select break; case MatchMode.IsolatedPhrase: - result = Regex.IsMatch(value, $@"(^|\s){Regex.Escape(searchTerm)}($|\s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + result = Regex.IsMatch(value, $@"(^|\b){Regex.Escape(searchTerm)}($|\b)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); break; case MatchMode.FullPhrase: