From cfd705ca68e56c4d75016ed6ed3793ae5feabe56 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 2 Jan 2026 23:33:45 +0800 Subject: [PATCH 1/8] Use Highlight matches from Everything To solve inaccurate highlight when facing Chinese Pinyin searches --- .../Search/Everything/EverythingAPI.cs | 53 ++++++++++++++++++- .../Everything/EverythingApiDllImport.cs | 2 +- .../Search/ResultManager.cs | 13 ++--- .../Search/SearchResult.cs | 7 +++ 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index a4e959dd9c4..4dee3fdbd79 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs @@ -157,7 +157,8 @@ public static async IAsyncEnumerable SearchAsync(EverythingSearchO Type = EverythingApiDllImport.Everything_IsFolderResult(idx) ? ResultType.Folder : EverythingApiDllImport.Everything_IsFileResult(idx) ? ResultType.File : ResultType.Volume, - Score = (int)EverythingApiDllImport.Everything_GetResultRunCount( (uint)idx) + Score = (int)EverythingApiDllImport.Everything_GetResultRunCount( (uint)idx), + HighlightData = EverythingHightlightStringToHighlightList(EverythingApiDllImport.Everything_GetResultHighlightedFileName((uint)idx)) }; yield return result; @@ -208,5 +209,55 @@ public static async Task IncrementRunCounterAsync(string fileOrFolder) } finally { _semaphore.Release(); } } + + /// + /// Convert the highlighted string from Everything API to a list of highlight indexes for our Result. + /// + /// Text inside a * quote is highlighted, two consecutive *'s is a single literal *. For example, in the highlighted text: abc*123* the 123 part is highlighted. + /// + public static List EverythingHightlightStringToHighlightList(string highlightString) + { + var highlightData = new List(); + + if (string.IsNullOrEmpty(highlightString)) + return highlightData; + + var isHighlighted = false; + var actualIndex = 0; // Index in the actual string (without * markers) + var length = highlightString.Length; + + for (var i = 0; i < length; i++) + { + if (highlightString[i] == '*') + { + // Check if it's a literal * (two consecutive *) + if (i + 1 < length && highlightString[i + 1] == '*') + { + // Two consecutive *'s represent a single literal * + if (isHighlighted) + { + highlightData.Add(actualIndex); + } + actualIndex++; + i++; // Skip the next * + } + else + { + isHighlighted = !isHighlighted; + } + } + else + { + // Regular character + if (isHighlighted) + { + highlightData.Add(actualIndex); + } + actualIndex++; + } + } + + return highlightData; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs index c952a980c47..f010f4dfe02 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingApiDllImport.cs @@ -147,7 +147,7 @@ public static void Load(string directory) [DllImport(DLL)] public static extern bool Everything_GetResultDateRecentlyChanged(uint nIndex, out long lpFileTime); [DllImport(DLL, CharSet = CharSet.Unicode)] - public static extern IntPtr Everything_GetResultHighlightedFileName(uint nIndex); + public static extern string Everything_GetResultHighlightedFileName(uint nIndex); [DllImport(DLL, CharSet = CharSet.Unicode)] public static extern IntPtr Everything_GetResultHighlightedPath(uint nIndex); [DllImport(DLL, CharSet = CharSet.Unicode)] diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 18eb168b9bd..8b4004eb974 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -64,9 +65,9 @@ public static Result CreateResult(Query query, SearchResult result) return result.Type switch { ResultType.Folder or ResultType.Volume => - CreateFolderResult(Path.GetFileName(result.FullPath), result.FullPath, result.FullPath, query, result.Score, result.WindowsIndexed), + CreateFolderResult(Path.GetFileName(result.FullPath), result.FullPath, result.FullPath, query, result.Score, result.WindowsIndexed, result.HighlightData), ResultType.File => - CreateFileResult(result.FullPath, query, result.Score, result.WindowsIndexed), + CreateFileResult(result.FullPath, query, result.Score, result.WindowsIndexed, result.HighlightData), _ => throw new ArgumentOutOfRangeException(null) }; } @@ -92,7 +93,7 @@ internal static void ShowNativeContextMenu(string path, ResultType type) } } - internal static Result CreateFolderResult(string title, string subtitle, string path, Query query, int score = 0, bool windowsIndexed = false) + internal static Result CreateFolderResult(string title, string subtitle, string path, Query query, int score = 0, bool windowsIndexed = false, List HighlightData = null) { return new Result { @@ -100,7 +101,7 @@ internal static Result CreateFolderResult(string title, string subtitle, string IcoPath = path, SubTitle = subtitle, AutoCompleteText = GetAutoCompleteText(title, query, path, ResultType.Folder), - TitleHighlightData = Context.API.FuzzySearch(query.Search, title).MatchData, + TitleHighlightData = HighlightData ?? Context.API.FuzzySearch(query.Search, title).MatchData, CopyText = path, Preview = new Result.PreviewInfo { @@ -282,7 +283,7 @@ internal static Result CreateOpenCurrentFolderResult(string path, string actionK }; } - internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false) + internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false, List HighlightData = null) { var isMedia = IsMedia(Path.GetExtension(filePath)); var title = Path.GetFileName(filePath) ?? string.Empty; @@ -302,7 +303,7 @@ internal static Result CreateFileResult(string filePath, Query query, int score FilePath = filePath, }, AutoCompleteText = GetAutoCompleteText(title, query, filePath, ResultType.File), - TitleHighlightData = Context.API.FuzzySearch(query.Search, title).MatchData, + TitleHighlightData = HighlightData ?? Context.API.FuzzySearch(query.Search, title).MatchData, Score = score, CopyText = filePath, PreviewPanel = new Lazy(() => new PreviewPanel(Settings, filePath, ResultType.File)), diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs index 3cd97df8277..14807293190 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs @@ -1,11 +1,18 @@ +using System.Collections.Generic; + namespace Flow.Launcher.Plugin.Explorer.Search { public record struct SearchResult { + public SearchResult() + { + } + public string FullPath { get; init; } public ResultType Type { get; init; } public int Score { get; init; } public bool WindowsIndexed { get; init; } + public List HighlightData { get; init; } = null; } } From 79bdd3c691a05884ce61b066c383c0ffcabaa008 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 3 Jan 2026 20:29:12 +0800 Subject: [PATCH 2/8] Fix typos --- .../Search/Everything/EverythingAPI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index 4dee3fdbd79..7225f61d66d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs @@ -158,7 +158,7 @@ public static async IAsyncEnumerable SearchAsync(EverythingSearchO EverythingApiDllImport.Everything_IsFileResult(idx) ? ResultType.File : ResultType.Volume, Score = (int)EverythingApiDllImport.Everything_GetResultRunCount( (uint)idx), - HighlightData = EverythingHightlightStringToHighlightList(EverythingApiDllImport.Everything_GetResultHighlightedFileName((uint)idx)) + HighlightData = EverythingHighlightStringToHighlightList(EverythingApiDllImport.Everything_GetResultHighlightedFileName((uint)idx)) }; yield return result; @@ -215,7 +215,7 @@ public static async Task IncrementRunCounterAsync(string fileOrFolder) /// /// Text inside a * quote is highlighted, two consecutive *'s is a single literal *. For example, in the highlighted text: abc*123* the 123 part is highlighted. /// - public static List EverythingHightlightStringToHighlightList(string highlightString) + public static List EverythingHighlightStringToHighlightList(string highlightString) { var highlightData = new List(); From 3129bddd24cf4e7377bf255e960b3e92db366b02 Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Sat, 3 Jan 2026 20:34:05 +0800 Subject: [PATCH 3/8] Update comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Search/Everything/EverythingAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index 7225f61d66d..0d42dbb8e54 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs @@ -214,7 +214,7 @@ public static async Task IncrementRunCounterAsync(string fileOrFolder) /// Convert the highlighted string from Everything API to a list of highlight indexes for our Result. /// /// Text inside a * quote is highlighted, two consecutive *'s is a single literal *. For example, in the highlighted text: abc*123* the 123 part is highlighted. - /// + /// A list of zero-based character indices that should be highlighted. public static List EverythingHighlightStringToHighlightList(string highlightString) { var highlightData = new List(); From 7e9dc186761d33a29faf9ea160822b096dbe7f5a Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Sat, 3 Jan 2026 20:34:51 +0800 Subject: [PATCH 4/8] Remove unnecessary constructor Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs index 14807293190..39e4119bfc6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs @@ -4,10 +4,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search { public record struct SearchResult { - public SearchResult() - { - } - public string FullPath { get; init; } public ResultType Type { get; init; } public int Score { get; init; } From 047d4ddc34b071ef3ff6002301648fbde5fbaa0f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 3 Jan 2026 20:36:01 +0800 Subject: [PATCH 5/8] Use camel case for parameters --- .../Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 8b4004eb974..60073ce5fd4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -93,7 +93,7 @@ internal static void ShowNativeContextMenu(string path, ResultType type) } } - internal static Result CreateFolderResult(string title, string subtitle, string path, Query query, int score = 0, bool windowsIndexed = false, List HighlightData = null) + internal static Result CreateFolderResult(string title, string subtitle, string path, Query query, int score = 0, bool windowsIndexed = false, List highlightData = null) { return new Result { @@ -101,7 +101,7 @@ internal static Result CreateFolderResult(string title, string subtitle, string IcoPath = path, SubTitle = subtitle, AutoCompleteText = GetAutoCompleteText(title, query, path, ResultType.Folder), - TitleHighlightData = HighlightData ?? Context.API.FuzzySearch(query.Search, title).MatchData, + TitleHighlightData = highlightData ?? Context.API.FuzzySearch(query.Search, title).MatchData, CopyText = path, Preview = new Result.PreviewInfo { @@ -283,7 +283,7 @@ internal static Result CreateOpenCurrentFolderResult(string path, string actionK }; } - internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false, List HighlightData = null) + internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false, List highlightData = null) { var isMedia = IsMedia(Path.GetExtension(filePath)); var title = Path.GetFileName(filePath) ?? string.Empty; @@ -303,7 +303,7 @@ internal static Result CreateFileResult(string filePath, Query query, int score FilePath = filePath, }, AutoCompleteText = GetAutoCompleteText(title, query, filePath, ResultType.File), - TitleHighlightData = HighlightData ?? Context.API.FuzzySearch(query.Search, title).MatchData, + TitleHighlightData = highlightData ?? Context.API.FuzzySearch(query.Search, title).MatchData, Score = score, CopyText = filePath, PreviewPanel = new Lazy(() => new PreviewPanel(Settings, filePath, ResultType.File)), From 43df5e174482e445c3874057d02c04ee9d31f7e3 Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Sat, 3 Jan 2026 20:37:16 +0800 Subject: [PATCH 6/8] Use ToInt32 instead of explicit converting Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Search/Everything/EverythingAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index 0d42dbb8e54..a786284699b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs @@ -157,7 +157,7 @@ public static async IAsyncEnumerable SearchAsync(EverythingSearchO Type = EverythingApiDllImport.Everything_IsFolderResult(idx) ? ResultType.Folder : EverythingApiDllImport.Everything_IsFileResult(idx) ? ResultType.File : ResultType.Volume, - Score = (int)EverythingApiDllImport.Everything_GetResultRunCount( (uint)idx), + Score = Convert.ToInt32(EverythingApiDllImport.Everything_GetResultRunCount((uint)idx)), HighlightData = EverythingHighlightStringToHighlightList(EverythingApiDllImport.Everything_GetResultHighlightedFileName((uint)idx)) }; From 10f160d9a2b07aa392a921a2a37f709fc07fa8fe Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 5 Jan 2026 09:43:48 +0800 Subject: [PATCH 7/8] Revert "Remove unnecessary constructor" This reverts commit 7e9dc186761d33a29faf9ea160822b096dbe7f5a. --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs index 39e4119bfc6..14807293190 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs @@ -4,6 +4,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search { public record struct SearchResult { + public SearchResult() + { + } + public string FullPath { get; init; } public ResultType Type { get; init; } public int Score { get; init; } From 43d3963e3dd09b67324b45f51e46c92e2570ecdb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 5 Jan 2026 09:45:32 +0800 Subject: [PATCH 8/8] Make SearchResult readonly and init HighlightData to empty list Changed SearchResult to a readonly record struct for better immutability and performance. Updated HighlightData to initialize as an empty list using a collection expression instead of null. Retained the default constructor with a clarifying comment. --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs index 14807293190..e2ff216cafd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs @@ -2,8 +2,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search { - public record struct SearchResult + public readonly record struct SearchResult { + // Constructor is necesssary for record struct public SearchResult() { } @@ -13,6 +14,6 @@ public SearchResult() public int Score { get; init; } public bool WindowsIndexed { get; init; } - public List HighlightData { get; init; } = null; + public List HighlightData { get; init; } = []; } }