Add support for matching full terms using suffixed !

This commit is contained in:
Dean Herbert 2023-06-27 15:34:33 +09:00
parent 9b08aaf93f
commit c423f77d53
2 changed files with 47 additions and 8 deletions

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using osu.Game.Beatmaps;
@ -62,7 +63,7 @@ namespace osu.Game.Screens.Select
string remainingText = value;
// First handle quoted segments to ensure we keep inline spaces in exact matches.
foreach (Match quotedSegment in Regex.Matches(searchText, "(\"[^\"]+\")"))
foreach (Match quotedSegment in Regex.Matches(searchText, "(\"[^\"]+\"[!]?)"))
{
terms.Add(new OptionalTextFilter { SearchTerm = quotedSegment.Value });
remainingText = remainingText.Replace(quotedSegment.Value, string.Empty);
@ -138,7 +139,7 @@ namespace osu.Game.Screens.Select
{
public bool HasFilter => !string.IsNullOrEmpty(SearchTerm);
public bool Exact { get; private set; }
public MatchMode MatchMode { get; private set; }
public bool Matches(string value)
{
@ -149,10 +150,18 @@ namespace osu.Game.Screens.Select
if (string.IsNullOrEmpty(value))
return false;
if (Exact)
return Regex.IsMatch(value, $@"(^|\s){Regex.Escape(searchTerm)}($|\s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
switch (MatchMode)
{
default:
case MatchMode.None:
return value.Contains(SearchTerm, StringComparison.InvariantCultureIgnoreCase);
return value.Contains(SearchTerm, StringComparison.InvariantCultureIgnoreCase);
case MatchMode.IsolatedPhrase:
return Regex.IsMatch(value, $@"(^|\s){Regex.Escape(searchTerm)}($|\s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
case MatchMode.FullPhrase:
return CultureInfo.InvariantCulture.CompareInfo.Compare(value, searchTerm, CompareOptions.IgnoreCase) == 0;
}
}
private string searchTerm;
@ -162,12 +171,42 @@ namespace osu.Game.Screens.Select
get => searchTerm;
set
{
searchTerm = value.Trim('"');
Exact = searchTerm != value;
searchTerm = value;
if (searchTerm.EndsWith("\"!", StringComparison.Ordinal))
{
searchTerm = searchTerm.Trim('!', '\"');
MatchMode = MatchMode.FullPhrase;
}
else if (searchTerm.StartsWith('\"'))
{
searchTerm = searchTerm.Trim('\"');
MatchMode = MatchMode.IsolatedPhrase;
}
else
MatchMode = MatchMode.None;
}
}
public bool Equals(OptionalTextFilter other) => SearchTerm == other.SearchTerm;
}
public enum MatchMode
{
/// <summary>
/// Match using a simple "contains" substring match.
/// </summary>
None,
/// <summary>
/// Match for the search phrase being isolated by spaces, or at the start or end of the text.
/// </summary>
IsolatedPhrase,
/// <summary>
/// Match for the search phrase matching the full text in completion.
/// </summary>
FullPhrase,
}
}
}

View File

@ -16,7 +16,7 @@ namespace osu.Game.Screens.Select
public static class FilterQueryParser
{
private static readonly Regex query_syntax_regex = new Regex(
@"\b(?<key>\w+)(?<op>(:|=|(>|<)(:|=)?))(?<value>("".*"")|(\S*))",
@"\b(?<key>\w+)(?<op>(:|=|(>|<)(:|=)?))(?<value>("".*""[!]?)|(\S*))",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
internal static void ApplyQueries(FilterCriteria criteria, string query)