From 2316c36a051034b5dba35aa36651b811f7eb2280 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 13 Jul 2025 23:29:41 +0800 Subject: [PATCH 1/5] Improve performance and readablility --- .../PinyinAlphabet.cs | 123 ++++++++++++------ .../TranslationMapping.cs | 20 +-- 2 files changed, 90 insertions(+), 53 deletions(-) diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs index b5344c7e914..3850a3bcb6d 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -14,11 +14,8 @@ namespace Flow.Launcher.Infrastructure { public class PinyinAlphabet : IAlphabet { - private ConcurrentDictionary _pinyinCache = - new(); - + private readonly ConcurrentDictionary _pinyinCache = new(); private readonly Settings _settings; - private ReadOnlyDictionary currentDoublePinyinTable; public PinyinAlphabet() @@ -44,105 +41,145 @@ public void Reload() private void CreateDoublePinyinTableFromStream(Stream jsonStream) { - Dictionary> table = JsonSerializer.Deserialize>>(jsonStream); - string schemaKey = _settings.DoublePinyinSchema.ToString(); // Convert enum to string - if (!table.TryGetValue(schemaKey, out var value)) + var table = JsonSerializer.Deserialize>>(jsonStream); + if (table == null) + { + throw new InvalidOperationException("Failed to deserialize double pinyin table: result is null"); + } + + var schemaKey = _settings.DoublePinyinSchema.ToString(); + if (!table.TryGetValue(schemaKey, out var schemaDict)) { - throw new ArgumentException("DoublePinyinSchema is invalid or double pinyin table is broken."); + throw new ArgumentException($"DoublePinyinSchema '{schemaKey}' is invalid or double pinyin table is broken."); } - currentDoublePinyinTable = new ReadOnlyDictionary(value); + + currentDoublePinyinTable = new ReadOnlyDictionary(schemaDict); } private void LoadDoublePinyinTable() { - if (_settings.UseDoublePinyin) + if (!_settings.UseDoublePinyin) { - var tablePath = Path.Join(AppContext.BaseDirectory, "Resources", "double_pinyin.json"); - try - { - using var fs = File.OpenRead(tablePath); - CreateDoublePinyinTableFromStream(fs); - } - catch (System.Exception e) - { - Log.Exception(nameof(PinyinAlphabet), "Failed to load double pinyin table from file: " + tablePath, e); - currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); - } + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + return; + } + + var tablePath = Path.Combine(AppContext.BaseDirectory, "Resources", "double_pinyin.json"); + try + { + using var fs = File.OpenRead(tablePath); + CreateDoublePinyinTableFromStream(fs); } - else + catch (FileNotFoundException e) { + Log.Exception(nameof(PinyinAlphabet), $"Double pinyin table file not found: {tablePath}", e); + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + } + catch (DirectoryNotFoundException e) + { + Log.Exception(nameof(PinyinAlphabet), $"Directory not found for double pinyin table: {tablePath}", e); + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + } + catch (UnauthorizedAccessException e) + { + Log.Exception(nameof(PinyinAlphabet), $"Access denied to double pinyin table: {tablePath}", e); + currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); + } + catch (System.Exception e) + { + Log.Exception(nameof(PinyinAlphabet), $"Failed to load double pinyin table from file: {tablePath}", e); currentDoublePinyinTable = new ReadOnlyDictionary(new Dictionary()); } } public bool ShouldTranslate(string stringToTranslate) { - // If a string has Chinese characters, we don't need to translate it to pinyin. - return _settings.ShouldUsePinyin && !WordsHelper.HasChinese(stringToTranslate); + // If the query (stringToTranslate) does NOT contain Chinese characters, + // we should translate the target string to pinyin for matching + return _settings.ShouldUsePinyin && !ContainsChinese(stringToTranslate); } public (string translation, TranslationMapping map) Translate(string content) { - if (!_settings.ShouldUsePinyin || !WordsHelper.HasChinese(content)) + if (!_settings.ShouldUsePinyin || !ContainsChinese(content)) return (content, null); - return _pinyinCache.TryGetValue(content, out var value) - ? value - : BuildCacheFromContent(content); + return _pinyinCache.TryGetValue(content, out var cached) ? cached : BuildCacheFromContent(content); } private (string translation, TranslationMapping map) BuildCacheFromContent(string content) { var resultList = WordsHelper.GetPinyinList(content); - - var resultBuilder = new StringBuilder(); + var resultBuilder = new StringBuilder(_settings.UseDoublePinyin ? 3 : 4); // Pre-allocate with estimated capacity var map = new TranslationMapping(); var previousIsChinese = false; for (var i = 0; i < resultList.Length; i++) { - if (content[i] >= 0x3400 && content[i] <= 0x9FD5) + if (IsChineseCharacter(content[i])) { - string translated = _settings.UseDoublePinyin ? ToDoublePin(resultList[i]) : resultList[i]; + var translated = _settings.UseDoublePinyin ? ToDoublePinyin(resultList[i]) : resultList[i]; + if (i > 0) { resultBuilder.Append(' '); } + map.AddNewIndex(resultBuilder.Length, translated.Length); resultBuilder.Append(translated); previousIsChinese = true; } else { + // Add space after Chinese characters before non-Chinese characters if (previousIsChinese) { previousIsChinese = false; resultBuilder.Append(' '); } + map.AddNewIndex(resultBuilder.Length, resultList[i].Length); resultBuilder.Append(resultList[i]); } } - map.endConstruct(); + map.EndConstruct(); - var key = resultBuilder.ToString(); - - return _pinyinCache[content] = (key, map); + var translation = resultBuilder.ToString(); + var result = (translation, map); + + return _pinyinCache[content] = result; } - #region Double Pinyin - - private string ToDoublePin(string fullPinyin) + /// + /// Optimized Chinese character detection using the comprehensive CJK Unicode ranges + /// + private static bool ContainsChinese(ReadOnlySpan text) { - if (currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue)) + foreach (var c in text) { - return doublePinyinValue; + if (IsChineseCharacter(c)) + return true; } - return fullPinyin; + return false; } - #endregion + /// + /// Check if a character is a Chinese character using comprehensive Unicode ranges + /// Covers CJK Unified Ideographs, Extension A + /// + private static bool IsChineseCharacter(char c) + { + return (c >= 0x4E00 && c <= 0x9FFF) || // CJK Unified Ideographs + (c >= 0x3400 && c <= 0x4DBF); // CJK Extension A + } + + private string ToDoublePinyin(string fullPinyin) + { + return currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue) + ? doublePinyinValue + : fullPinyin; + } } } diff --git a/Flow.Launcher.Infrastructure/TranslationMapping.cs b/Flow.Launcher.Infrastructure/TranslationMapping.cs index 951979fa770..13d8342acd6 100644 --- a/Flow.Launcher.Infrastructure/TranslationMapping.cs +++ b/Flow.Launcher.Infrastructure/TranslationMapping.cs @@ -6,31 +6,31 @@ namespace Flow.Launcher.Infrastructure { public class TranslationMapping { - private bool constructed; + private bool _isConstructed; // Assuming one original item maps to multi translated items // list[i] is the last translated index + 1 of original index i - private readonly List originalToTranslated = new List(); + private readonly List _originalToTranslated = new(); public void AddNewIndex(int translatedIndex, int length) { - if (constructed) - throw new InvalidOperationException("Mapping shouldn't be changed after constructed"); + if (_isConstructed) + throw new InvalidOperationException("Mapping shouldn't be changed after construction"); - originalToTranslated.Add(translatedIndex + length); + _originalToTranslated.Add(translatedIndex + length); } public int MapToOriginalIndex(int translatedIndex) { - int loc = originalToTranslated.BinarySearch(translatedIndex); - return loc >= 0 ? loc : ~loc; + var searchResult = _originalToTranslated.BinarySearch(translatedIndex); + return searchResult >= 0 ? searchResult : ~searchResult; } - public void endConstruct() + public void EndConstruct() { - if (constructed) + if (_isConstructed) throw new InvalidOperationException("Mapping has already been constructed"); - constructed = true; + _isConstructed = true; } } } From 071b75bcb585ae9109e65d525b72a5612570a597 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 09:12:14 +0800 Subject: [PATCH 2/5] Improve code quality --- Flow.Launcher.Infrastructure/PinyinAlphabet.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs index 3850a3bcb6d..cc4eccdc52c 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -41,11 +41,8 @@ public void Reload() private void CreateDoublePinyinTableFromStream(Stream jsonStream) { - var table = JsonSerializer.Deserialize>>(jsonStream); - if (table == null) - { + var table = JsonSerializer.Deserialize>>(jsonStream) ?? throw new InvalidOperationException("Failed to deserialize double pinyin table: result is null"); - } var schemaKey = _settings.DoublePinyinSchema.ToString(); if (!table.TryGetValue(schemaKey, out var schemaDict)) From d7d3549c828f8845dccdc62ff1b233b0caad0d45 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 09:17:32 +0800 Subject: [PATCH 3/5] Improve code quality --- .../TranslationMapping.cs | 2 -- Flow.Launcher.Test/TranslationMappingTest.cs | 17 +++++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Infrastructure/TranslationMapping.cs b/Flow.Launcher.Infrastructure/TranslationMapping.cs index 395b4a4b28d..b4c6764df1a 100644 --- a/Flow.Launcher.Infrastructure/TranslationMapping.cs +++ b/Flow.Launcher.Infrastructure/TranslationMapping.cs @@ -11,12 +11,10 @@ public class TranslationMapping // list[i] is the last translated index + 1 of original index i private readonly List _originalToTranslated = new(); - public void AddNewIndex(int translatedIndex, int length) { if (_isConstructed) throw new InvalidOperationException("Mapping shouldn't be changed after construction"); - _originalToTranslated.Add(translatedIndex + length); } diff --git a/Flow.Launcher.Test/TranslationMappingTest.cs b/Flow.Launcher.Test/TranslationMappingTest.cs index 10d765f5ae4..829dbee818a 100644 --- a/Flow.Launcher.Test/TranslationMappingTest.cs +++ b/Flow.Launcher.Test/TranslationMappingTest.cs @@ -1,4 +1,6 @@ -using Flow.Launcher.Infrastructure; +using System.Collections.Generic; +using System.Reflection; +using Flow.Launcher.Infrastructure; using NUnit.Framework; using NUnit.Framework.Legacy; @@ -34,22 +36,21 @@ public void MapToOriginalIndex_ShouldReturnExpectedIndex(int translatedIndex, in mapping.AddNewIndex(2, 2); mapping.AddNewIndex(5, 3); - var result = mapping.MapToOriginalIndex(translatedIndex); ClassicAssert.AreEqual(expectedOriginalIndex, result); } - private int GetOriginalToTranslatedCount(TranslationMapping mapping) + private static int GetOriginalToTranslatedCount(TranslationMapping mapping) { - var field = typeof(TranslationMapping).GetField("originalToTranslated", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - var list = (System.Collections.Generic.List)field.GetValue(mapping); + var field = typeof(TranslationMapping).GetField("originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); + var list = (List)field.GetValue(mapping); return list.Count; } - private int GetOriginalToTranslatedAt(TranslationMapping mapping, int index) + private static int GetOriginalToTranslatedAt(TranslationMapping mapping, int index) { - var field = typeof(TranslationMapping).GetField("originalToTranslated", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - var list = (System.Collections.Generic.List)field.GetValue(mapping); + var field = typeof(TranslationMapping).GetField("originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); + var list = (List)field.GetValue(mapping); return list[index]; } } From 88a6e59f46c2789d85d2b4cdf81c0a6ef317c1a7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 14 Jul 2025 09:19:46 +0800 Subject: [PATCH 4/5] Fix test project field issue --- Flow.Launcher.Test/TranslationMappingTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Test/TranslationMappingTest.cs b/Flow.Launcher.Test/TranslationMappingTest.cs index 829dbee818a..bd3636f0ad8 100644 --- a/Flow.Launcher.Test/TranslationMappingTest.cs +++ b/Flow.Launcher.Test/TranslationMappingTest.cs @@ -42,14 +42,14 @@ public void MapToOriginalIndex_ShouldReturnExpectedIndex(int translatedIndex, in private static int GetOriginalToTranslatedCount(TranslationMapping mapping) { - var field = typeof(TranslationMapping).GetField("originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); + var field = typeof(TranslationMapping).GetField("_originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); var list = (List)field.GetValue(mapping); return list.Count; } private static int GetOriginalToTranslatedAt(TranslationMapping mapping, int index) { - var field = typeof(TranslationMapping).GetField("originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); + var field = typeof(TranslationMapping).GetField("_originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance); var list = (List)field.GetValue(mapping); return list[index]; } From bdf5ccd3a97459ae69f25ffe9023a63d9e94e1a0 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 14 Jul 2025 09:28:52 +0800 Subject: [PATCH 5/5] Add word --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 5b3419041a6..0fea6d9ab24 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -103,3 +103,4 @@ Reloadable metadatas WMP VSTHRD +CJK