From bd32454341b25e50eaf80fb972b461c44ae5b518 Mon Sep 17 00:00:00 2001 From: User <42337050+com-master@users.noreply.github.com> Date: Mon, 15 Jun 2026 06:45:49 +0300 Subject: [PATCH] search char equivalences unicode --- .../EntitySpawningUIController.cs | 6 +-- .../TileSpawningUIController.cs | 4 +- Robust.Shared/Utility/SearchHelpers.cs | 41 +++++++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 Robust.Shared/Utility/SearchHelpers.cs diff --git a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs index dc596664d..ad03d3bdf 100644 --- a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs +++ b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs @@ -241,17 +241,17 @@ private static bool DoesEntityMatchSearch(EntityPrototype prototype, string sear if (string.IsNullOrEmpty(searchStr)) return true; - if (prototype.ID.Contains(searchStr, StringComparison.InvariantCultureIgnoreCase)) + if (prototype.ID.ContainsSearch(searchStr)) return true; if (prototype.EditorSuffix != null && - prototype.EditorSuffix.Contains(searchStr, StringComparison.InvariantCultureIgnoreCase)) + prototype.EditorSuffix.ContainsSearch(searchStr)) return true; if (string.IsNullOrEmpty(prototype.Name)) return false; - if (prototype.Name.Contains(searchStr, StringComparison.InvariantCultureIgnoreCase)) + if (prototype.Name.ContainsSearch(searchStr)) return true; return false; diff --git a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs index 3669b2491..ba49b1cff 100644 --- a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs +++ b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs @@ -221,8 +221,8 @@ private void BuildTileList(string? searchStr = null) if (!string.IsNullOrEmpty(searchStr)) { tileDefs = tileDefs.Where(s => - Loc.GetString(s.Name).Contains(searchStr, StringComparison.CurrentCultureIgnoreCase) || - s.ID.Contains(searchStr, StringComparison.OrdinalIgnoreCase)); + Loc.GetString(s.Name).ContainsSearch(searchStr) || + s.ID.ContainsSearch(searchStr)); } tileDefs = tileDefs.OrderBy(d => Loc.GetString(d.Name)); diff --git a/Robust.Shared/Utility/SearchHelpers.cs b/Robust.Shared/Utility/SearchHelpers.cs new file mode 100644 index 000000000..c636bfed9 --- /dev/null +++ b/Robust.Shared/Utility/SearchHelpers.cs @@ -0,0 +1,41 @@ +using System.Globalization; + +namespace Robust.Shared.Utility; + +/// +/// Helpers for user-facing text search that use Unicode-aware comparison, so that characters +/// differing only by diacritical marks are treated as equivalent. +/// +/// +/// Uses , , +/// and via . This means: +/// +/// Characters that differ only by diacritical marks are equivalent: Russian ё matches е, +/// German ü matches u, French é/è/ê all match e, Spanish ñ matches n, etc. +/// Full-width and half-width variants of the same character are equivalent (relevant for +/// Japanese and Chinese input). +/// Hiragana and Katakana representations of the same sound are equivalent. +/// +/// Uses the current culture so that locale-specific comparison rules are respected. +/// No configuration or setup is required; any UI code can call directly. +/// +public static class SearchHelpers +{ + private const CompareOptions SearchOptions = + CompareOptions.IgnoreCase | + CompareOptions.IgnoreNonSpace | + CompareOptions.IgnoreWidth | + CompareOptions.IgnoreKanaType; + + /// + /// Returns if contains + /// , ignoring case, diacritical marks, character width, and + /// kana type. + /// + public static bool ContainsSearch(this string source, string search) + { + if (string.IsNullOrEmpty(search)) return true; + if (string.IsNullOrEmpty(source)) return false; + return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, search, SearchOptions) >= 0; + } +}