mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 17:47:29 +08:00
Merge pull request #19275 from solstice23/search-filter
Add multiple-units support in search length criteria
This commit is contained in:
commit
7a62544923
@ -118,17 +118,31 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
Assert.IsNull(filterCriteria.BPM.Max);
|
||||
}
|
||||
|
||||
private static readonly object[] length_query_examples =
|
||||
private static readonly object[] correct_length_query_examples =
|
||||
{
|
||||
new object[] { "6ms", TimeSpan.FromMilliseconds(6), TimeSpan.FromMilliseconds(1) },
|
||||
new object[] { "23s", TimeSpan.FromSeconds(23), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "9m", TimeSpan.FromMinutes(9), TimeSpan.FromMinutes(1) },
|
||||
new object[] { "0.25h", TimeSpan.FromHours(0.25), TimeSpan.FromHours(1) },
|
||||
new object[] { "70", TimeSpan.FromSeconds(70), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "7m27s", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "7:27", TimeSpan.FromSeconds(447), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "1h2m3s", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "1h2m3.5s", TimeSpan.FromSeconds(3723.5), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "1:2:3", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "1:02:03", TimeSpan.FromSeconds(3723), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "6", TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "6.5", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "6.5s", TimeSpan.FromSeconds(6.5), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "6.5m", TimeSpan.FromMinutes(6.5), TimeSpan.FromMinutes(1) },
|
||||
new object[] { "6h5m", TimeSpan.FromMinutes(365), TimeSpan.FromMinutes(1) },
|
||||
new object[] { "65m", TimeSpan.FromMinutes(65), TimeSpan.FromMinutes(1) },
|
||||
new object[] { "90s", TimeSpan.FromSeconds(90), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "80m20s", TimeSpan.FromSeconds(4820), TimeSpan.FromSeconds(1) },
|
||||
new object[] { "1h20s", TimeSpan.FromSeconds(3620), TimeSpan.FromSeconds(1) },
|
||||
};
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(length_query_examples))]
|
||||
[TestCaseSource(nameof(correct_length_query_examples))]
|
||||
public void TestApplyLengthQueries(string lengthQuery, TimeSpan expectedLength, TimeSpan scale)
|
||||
{
|
||||
string query = $"length={lengthQuery} time";
|
||||
@ -140,6 +154,29 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
Assert.AreEqual(expectedLength.TotalMilliseconds + scale.TotalMilliseconds / 2.0, filterCriteria.Length.Max);
|
||||
}
|
||||
|
||||
private static readonly object[] incorrect_length_query_examples =
|
||||
{
|
||||
new object[] { "7.5m27s" },
|
||||
new object[] { "7m27" },
|
||||
new object[] { "7m7m7m" },
|
||||
new object[] { "7m70s" },
|
||||
new object[] { "5s6m" },
|
||||
new object[] { "0:" },
|
||||
new object[] { ":0" },
|
||||
new object[] { "0:3:" },
|
||||
new object[] { "3:15.5" },
|
||||
};
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(incorrect_length_query_examples))]
|
||||
public void TestInvalidLengthQueries(string lengthQuery)
|
||||
{
|
||||
string query = $"length={lengthQuery} time";
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual(false, filterCriteria.Length.HasFilter);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestApplyDivisorQueries()
|
||||
{
|
||||
|
@ -1,9 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
@ -125,10 +124,24 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
if (Enum.TryParse(value, true, out result)) return true;
|
||||
|
||||
value = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture));
|
||||
string? prefixMatch = Enum.GetNames(typeof(TEnum)).FirstOrDefault(name => name.StartsWith(value, true, CultureInfo.InvariantCulture));
|
||||
|
||||
if (prefixMatch == null)
|
||||
return false;
|
||||
|
||||
return Enum.TryParse(value, true, out result);
|
||||
}
|
||||
|
||||
private static GroupCollection? tryMatchRegex(string value, string regex)
|
||||
{
|
||||
Match matches = Regex.Match(value, regex);
|
||||
|
||||
if (matches.Success)
|
||||
return matches.Groups;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse a keyword filter with the specified <paramref name="op"/> and textual <paramref name="value"/>.
|
||||
/// If the value indicates a valid textual filter, the function returns <c>true</c> and the resulting data is stored into
|
||||
@ -312,11 +325,45 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private static bool tryUpdateLengthRange(FilterCriteria criteria, Operator op, string val)
|
||||
{
|
||||
if (!tryParseDoubleWithPoint(val.TrimEnd('m', 's', 'h'), out double length))
|
||||
List<string> parts = new List<string>();
|
||||
|
||||
GroupCollection? match = null;
|
||||
|
||||
match ??= tryMatchRegex(val, @"^((?<hours>\d+):)?(?<minutes>\d+):(?<seconds>\d+)$");
|
||||
match ??= tryMatchRegex(val, @"^((?<hours>\d+(\.\d+)?)h)?((?<minutes>\d+(\.\d+)?)m)?((?<seconds>\d+(\.\d+)?)s)?$");
|
||||
match ??= tryMatchRegex(val, @"^(?<seconds>\d+(\.\d+)?)$");
|
||||
|
||||
if (match == null)
|
||||
return false;
|
||||
|
||||
int scale = getLengthScale(val);
|
||||
return tryUpdateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0);
|
||||
if (match["seconds"].Success)
|
||||
parts.Add(match["seconds"].Value + "s");
|
||||
if (match["minutes"].Success)
|
||||
parts.Add(match["minutes"].Value + "m");
|
||||
if (match["hours"].Success)
|
||||
parts.Add(match["hours"].Value + "h");
|
||||
|
||||
double totalLength = 0;
|
||||
int minScale = 3600000;
|
||||
|
||||
for (int i = 0; i < parts.Count; i++)
|
||||
{
|
||||
string part = parts[i];
|
||||
string partNoUnit = part.TrimEnd('m', 's', 'h');
|
||||
if (!tryParseDoubleWithPoint(partNoUnit, out double length))
|
||||
return false;
|
||||
|
||||
if (i != parts.Count - 1 && length >= 60)
|
||||
return false;
|
||||
if (i != 0 && partNoUnit.Contains('.'))
|
||||
return false;
|
||||
|
||||
int scale = getLengthScale(part);
|
||||
totalLength += length * scale;
|
||||
minScale = Math.Min(minScale, scale);
|
||||
}
|
||||
|
||||
return tryUpdateCriteriaRange(ref criteria.Length, op, totalLength, minScale / 2.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user