diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs index a4e959dd9c4..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,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 = Convert.ToInt32(EverythingApiDllImport.Everything_GetResultRunCount((uint)idx)), + HighlightData = EverythingHighlightStringToHighlightList(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. + /// A list of zero-based character indices that should be highlighted. + public static List EverythingHighlightStringToHighlightList(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..60073ce5fd4 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..e2ff216cafd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs @@ -1,11 +1,19 @@ +using System.Collections.Generic; + namespace Flow.Launcher.Plugin.Explorer.Search { - public record struct SearchResult + public readonly record struct SearchResult { + // Constructor is necesssary for record struct + 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; } = []; } }