diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs index eb994a6f9..bb2fc4425 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -72,14 +73,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything } } - public async IAsyncEnumerable SearchAsync(string search, [EnumeratorCancellation] CancellationToken token) + public async IAsyncEnumerable SearchAsync(string search, [EnumeratorCancellation] CancellationToken token, IEnumerable allowedResultTypes = null) { await ThrowIfEverythingNotAvailableAsync(token); if (token.IsCancellationRequested) yield break; - var option = new EverythingSearchOption(search, + var searchKeyword = BuildSearchKeywordWithTypeFilter(search, allowedResultTypes); + + var option = new EverythingSearchOption(searchKeyword, Settings.SortOption, MaxCount: Settings.MaxResult, IsFullPathSearch: Settings.EverythingSearchFullPath, @@ -89,6 +92,33 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything yield return result; } + private static string BuildSearchKeywordWithTypeFilter(string search, IEnumerable allowedResultTypes) + { + if (allowedResultTypes == null) + return search; + + var typesList = allowedResultTypes as IList ?? allowedResultTypes.ToList(); + if (typesList.Count == 0 || typesList.Count == 3) + return search; // No filtering needed + + var hasFile = typesList.Contains(ResultType.File); + var hasFolder = typesList.Contains(ResultType.Folder); + var hasVolume = typesList.Contains(ResultType.Volume); + + var filter = (hasFile, hasFolder, hasVolume) switch + { + (true, false, false) => "file:", + (false, true, false) => "folder:", + (false, false, true) => "volume:", + (true, true, false) => "", + (true, false, true) => "", + (false, true, true) => "", + _ => null + }; + + return filter == null ? search : $"{filter} {search}"; + } + public async IAsyncEnumerable ContentSearchAsync(string plainSearch, string contentSearch, [EnumeratorCancellation] CancellationToken token) { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IProvider/IIndexProvider.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IProvider/IIndexProvider.cs index 9909b18d8..ae0f83621 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IProvider/IIndexProvider.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IProvider/IIndexProvider.cs @@ -3,8 +3,18 @@ using System.Threading; namespace Flow.Launcher.Plugin.Explorer.Search.IProvider { + /// + /// Provides functionality for searching indexed items. + /// public interface IIndexProvider { - public IAsyncEnumerable SearchAsync(string search, CancellationToken token); + /// + /// Asynchronously searches for items matching the specified search criteria. + /// + /// The search query string. + /// The cancellation token to cancel the search operation. + /// Optional collection of result types to filter the search results. If null, all result types are included. + /// An asynchronous enumerable of objects matching the search criteria. + public IAsyncEnumerable SearchAsync(string search, CancellationToken token, IEnumerable allowedResultTypes = null); } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 9420e3d3a..a70a62db3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -132,7 +132,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search case false when CanUseIndexSearchByActionKeywords(activeActionKeywords): - searchResults = Settings.IndexProvider.SearchAsync(query.Search, token); + searchResults = Settings.IndexProvider.SearchAsync(query.Search, token, GetAllowedResultTypeByUsedActionKeyword(activeActionKeywords)); engineName = Enum.GetName(Settings.IndexSearchEngine); break; default: @@ -150,7 +150,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search { if (search.Type == ResultType.File && IsExcludedFile(search)) continue; - + // TODO: Optimize filtering by action keyword at the provider level to reduce unnecessary searches. + // 1. Path search and content search may not need filtering as they are specific enough. + // 2. Index search can be optimized by passing allowed result types to the provider to limit the search scope. + // 3. Quick access link filtering is already handled separately. + // if (IsResultTypeFilteredByActionKeyword(search.Type, actions)) continue; @@ -329,6 +333,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search return true; } + private List GetAllowedResultTypeByUsedActionKeyword(Dictionary activeActionKeywords) + { + List activeKeywords = activeActionKeywords.Keys.ToList(); + var allowedResultTypes = new List(); + foreach (var actionKeyword in activeKeywords) + { + if (_allowedTypesByActionKeyword.TryGetValue(actionKeyword, out var resultTypes)) + { + allowedResultTypes.AddRange(resultTypes); + } + } + return allowedResultTypes.Distinct().ToList(); + } + private bool CanUseIndexSearchByActionKeywords(Dictionary actions) { var keysToUseIndexSearch = new[] diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs index eeb5c2c4a..0201fa3b7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs @@ -83,8 +83,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex } } - public IAsyncEnumerable SearchAsync(string search, CancellationToken token) + public IAsyncEnumerable SearchAsync(string search, CancellationToken token, IEnumerable allowedResultTypes = null) { + // TODO: result type filter return WindowsIndexFilesAndFoldersSearchAsync(search, token: token); }