diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index c40040df5be..43a34c8a6e5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -32,6 +32,7 @@ Add General Setting Customise Action Keywords + Exclude Quick Access results when using action keywords Customise Quick Access Quick Access Links Everything Setting @@ -58,6 +59,8 @@ File Content Search: Index Search: Quick Access: + Folder Search: + File Search: Current Action Keyword Done Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index d7b0690828a..235994cbacf 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,13 +1,14 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; -using Flow.Launcher.Plugin.Explorer.Search.Everything; -using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; -using Flow.Launcher.Plugin.SharedCommands; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Plugin.Explorer.Exceptions; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.Everything; +using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; +using Flow.Launcher.Plugin.SharedCommands; +using static Flow.Launcher.Plugin.Explorer.Settings; using Path = System.IO.Path; namespace Flow.Launcher.Plugin.Explorer.Search @@ -47,45 +48,43 @@ public int GetHashCode(Result obj) internal async Task> SearchAsync(Query query, CancellationToken token) { var results = new HashSet(PathEqualityComparator.Instance); + var keyword = query.ActionKeyword.Length == 0 ? Query.GlobalPluginWildcardSign : query.ActionKeyword; + var isPathSearch = query.Search.IsLocationPathString() + || EnvironmentVariables.IsEnvironmentVariableSearch(query.Search) + || EnvironmentVariables.HasEnvironmentVar(query.Search); - // This allows the user to type the below action keywords and see/search the list of quick folder links - if (ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword)) + // If action keyword is enabled and matched, get the active action keyword. + var activeActionKeyword = Settings.GetActiveActionKeyword(keyword); + + // No action keyword matched - plugin should not handle this query, return empty results. + if (activeActionKeyword == null && !isPathSearch) { - if (string.IsNullOrEmpty(query.Search) && ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword)) - return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks); + return []; } - else + + // If no action keyword matched but the query is a path search, set active action keyword to path search. + if (activeActionKeyword == null && isPathSearch) activeActionKeyword = ActionKeyword.PathSearchActionKeyword; + + // This allows the user to type the below action keywords and see/search the list of quick folder links + if (string.IsNullOrEmpty(query.Search) + && activeActionKeyword.Equals(ActionKeyword.QuickAccessActionKeyword)) { - // No action keyword matched- plugin should not handle this query, return empty results. - return new List(); + return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks); } IAsyncEnumerable searchResults; - bool isPathSearch = query.Search.IsLocationPathString() - || EnvironmentVariables.IsEnvironmentVariableSearch(query.Search) - || EnvironmentVariables.HasEnvironmentVar(query.Search); - string engineName; - switch (isPathSearch) + switch (activeActionKeyword.Equals(ActionKeyword.PathSearchActionKeyword)) { - case true - when ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword): - + case true: results.UnionWith(await PathSearchAsync(query, token).ConfigureAwait(false)); - - return results.ToList(); + return [.. results]; case false - when ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword): - // Intentionally require enabling of Everything's content search due to its slowness + when activeActionKeyword.Equals(ActionKeyword.FileContentSearchActionKeyword): if (Settings.ContentIndexProvider is EverythingSearchManager && !Settings.EnableEverythingContentSearch) return EverythingContentSearchResult(query); @@ -94,36 +93,31 @@ when ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKey break; case false - when ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword): + when activeActionKeyword.Equals(ActionKeyword.QuickAccessActionKeyword): + return QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); + default: searchResults = Settings.IndexProvider.SearchAsync(query.Search, token); engineName = Enum.GetName(Settings.IndexSearchEngine); break; - - case true or false - when ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword): - return QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); - - default: - return results.ToList(); } // Merge Quick Access Link results for non-path searches. - results.UnionWith(QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks)); - + MergeQuickAccessInResultsIfQueryMatch(results, query, activeActionKeyword); try { await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false)) - if (search.Type == ResultType.File && IsExcludedFile(search)) { + { + if (ShouldSkip(activeActionKeyword!.Value, search)) + { continue; - } else { - results.Add(ResultManager.CreateResult(query, search)); } + results.Add(ResultManager.CreateResult(query, search)); + } } catch (OperationCanceledException) { - return new List(); + return []; } catch (EngineNotAvailableException) { @@ -137,33 +131,13 @@ when ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword): results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any( excludedPath => FilesFolders.PathContains(excludedPath.Path, r.SubTitle, allowEqual: true))); - return results.ToList(); - } - - private bool ActionKeywordMatch(Query query, Settings.ActionKeyword allowedActionKeyword) - { - var keyword = query.ActionKeyword.Length == 0 ? Query.GlobalPluginWildcardSign : query.ActionKeyword; - - return allowedActionKeyword switch - { - Settings.ActionKeyword.SearchActionKeyword => Settings.SearchActionKeywordEnabled && - keyword == Settings.SearchActionKeyword, - Settings.ActionKeyword.PathSearchActionKeyword => Settings.PathSearchKeywordEnabled && - keyword == Settings.PathSearchActionKeyword, - Settings.ActionKeyword.FileContentSearchActionKeyword => Settings.FileContentSearchKeywordEnabled && - keyword == Settings.FileContentSearchActionKeyword, - Settings.ActionKeyword.IndexSearchActionKeyword => Settings.IndexSearchKeywordEnabled && - keyword == Settings.IndexSearchActionKeyword, - Settings.ActionKeyword.QuickAccessActionKeyword => Settings.QuickAccessKeywordEnabled && - keyword == Settings.QuickAccessActionKeyword, - _ => throw new ArgumentOutOfRangeException(nameof(allowedActionKeyword), allowedActionKeyword, "actionKeyword out of range") - }; + return [.. results]; } private List EverythingContentSearchResult(Query query) { - return new List() - { + return + [ new() { Title = Localize.flowlauncher_plugin_everything_enable_content_search(), @@ -176,7 +150,7 @@ private List EverythingContentSearchResult(Query query) return false; } } - }; + ]; } private async Task> PathSearchAsync(Query query, CancellationToken token = default) @@ -197,7 +171,7 @@ private async Task> PathSearchAsync(Query query, CancellationToken // Check that actual location exists, otherwise directory search will throw directory not found exception if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(path).LocationExists()) - return results.ToList(); + return [.. results]; var useIndexSearch = Settings.IndexSearchEngine is Settings.IndexSearchEngineOption.WindowsIndex && UseWindowsIndexForDirectorySearch(path); @@ -209,7 +183,7 @@ private async Task> PathSearchAsync(Query query, CancellationToken : ResultManager.CreateOpenCurrentFolderResult(retrievedDirectoryPath, query.ActionKeyword, useIndexSearch)); if (token.IsCancellationRequested) - return new List(); + return []; IAsyncEnumerable directoryResult; @@ -231,7 +205,7 @@ private async Task> PathSearchAsync(Query query, CancellationToken } if (token.IsCancellationRequested) - return new List(); + return []; try { @@ -246,14 +220,14 @@ private async Task> PathSearchAsync(Query query, CancellationToken } - return results.ToList(); + return [.. results]; } public bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword; public static bool UseIndexSearch(string path) { - if (Main.Settings.IndexSearchEngine is not Settings.IndexSearchEngineOption.WindowsIndex) + if (Main.Settings.IndexSearchEngine is not IndexSearchEngineOption.WindowsIndex) return false; // Check if the path is using windows index search @@ -275,10 +249,48 @@ private bool UseWindowsIndexForDirectorySearch(string locationPath) private bool IsExcludedFile(SearchResult result) { - string[] excludedFileTypes = Settings.ExcludedFileTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + string[] excludedFileTypes = Settings.ExcludedFileTypes.Split([','], StringSplitOptions.RemoveEmptyEntries); string fileExtension = Path.GetExtension(result.FullPath).TrimStart('.'); return excludedFileTypes.Contains(fileExtension, StringComparer.OrdinalIgnoreCase); } + + private bool ShouldSkip(ActionKeyword actionKeywordActive, SearchResult search) + { + // Is excluded file type + if (search.Type == ResultType.File && IsExcludedFile(search)) + { + return true; + } + + // Action keyword specific filtering for folders + if (actionKeywordActive.Equals(ActionKeyword.FolderSearchActionKeyword) + && search.Type != ResultType.Folder) + { + return true; + } + + // Action keyword specific filtering for files + if (actionKeywordActive.Equals(ActionKeyword.FileSearchActionKeyword) + && search.Type != ResultType.File) + { + return true; + } + + return false; + } + + private void MergeQuickAccessInResultsIfQueryMatch(HashSet results, Query query, ActionKeyword? activeActionKeyword) + { + if (activeActionKeyword != null + && activeActionKeyword != ActionKeyword.QuickAccessActionKeyword + && Settings.ExcludeQuickAccessFromActionKeywords) + { + return; + } + + var quickAccessMatched = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); + if (quickAccessMatched != null && quickAccessMatched.Count > 0) results.UnionWith(quickAccessMatched); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 8d62531cd62..b4eda373797 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,12 +1,13 @@ -using Flow.Launcher.Plugin.Explorer.Search; -using Flow.Launcher.Plugin.Explorer.Search.Everything; -using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; -using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; -using System; +using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Text.Json.Serialization; +using Flow.Launcher.Plugin.Explorer.Search; +using Flow.Launcher.Plugin.Explorer.Search.Everything; using Flow.Launcher.Plugin.Explorer.Search.IProvider; +using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; +using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; namespace Flow.Launcher.Plugin.Explorer { @@ -58,6 +59,17 @@ public class Settings public bool QuickAccessKeywordEnabled { get; set; } + + public string FolderSearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; + + public bool FolderSearchKeywordEnabled { get; set; } + + public string FileSearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign; + + public bool FileSearchKeywordEnabled { get; set; } + + public bool ExcludeQuickAccessFromActionKeywords { get; set; } = false; + public bool WarnWindowsSearchServiceOff { get; set; } = true; public bool ShowFileSizeInPreviewPanel { get; set; } = true; @@ -154,13 +166,15 @@ public enum ContentIndexSearchEngineOption #endregion - internal enum ActionKeyword + public enum ActionKeyword { SearchActionKeyword, PathSearchActionKeyword, FileContentSearchActionKeyword, IndexSearchActionKeyword, - QuickAccessActionKeyword + QuickAccessActionKeyword, + FolderSearchActionKeyword, + FileSearchActionKeyword, } internal string GetActionKeyword(ActionKeyword actionKeyword) => actionKeyword switch @@ -170,6 +184,8 @@ internal enum ActionKeyword ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword, ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword, ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword, + ActionKeyword.FolderSearchActionKeyword => FolderSearchActionKeyword, + ActionKeyword.FileSearchActionKeyword => FileSearchActionKeyword, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyWord property not found") }; @@ -180,6 +196,8 @@ internal enum ActionKeyword ActionKeyword.FileContentSearchActionKeyword => FileContentSearchActionKeyword = keyword, ActionKeyword.IndexSearchActionKeyword => IndexSearchActionKeyword = keyword, ActionKeyword.QuickAccessActionKeyword => QuickAccessActionKeyword = keyword, + ActionKeyword.FolderSearchActionKeyword => FolderSearchActionKeyword = keyword, + ActionKeyword.FileSearchActionKeyword => FileSearchActionKeyword = keyword, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyWord property not found") }; @@ -190,6 +208,8 @@ internal enum ActionKeyword ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled, ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled, ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled, + ActionKeyword.FolderSearchActionKeyword => FolderSearchKeywordEnabled, + ActionKeyword.FileSearchActionKeyword => FileSearchKeywordEnabled, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyword enabled status not defined") }; @@ -200,7 +220,22 @@ internal enum ActionKeyword ActionKeyword.IndexSearchActionKeyword => IndexSearchKeywordEnabled = enable, ActionKeyword.FileContentSearchActionKeyword => FileContentSearchKeywordEnabled = enable, ActionKeyword.QuickAccessActionKeyword => QuickAccessKeywordEnabled = enable, + ActionKeyword.FolderSearchActionKeyword => FolderSearchKeywordEnabled = enable, + ActionKeyword.FileSearchActionKeyword => FileSearchKeywordEnabled = enable, _ => throw new ArgumentOutOfRangeException(nameof(actionKeyword), actionKeyword, "ActionKeyword enabled status not defined") }; + + public ActionKeyword? GetActiveActionKeyword(string actionKeywordStr) + { + if (string.IsNullOrEmpty(actionKeywordStr)) return null; + foreach (var action in Enum.GetValues()) + { + var keywordStr = GetActionKeyword(action); + if (string.IsNullOrEmpty(keywordStr)) continue; + var isEnabled = GetActionKeywordEnabled(action); + if (keywordStr == actionKeywordStr && isEnabled) return action; + } + return null; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index 2d46c6307cc..956c84db2c6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -279,7 +279,11 @@ private void InitializeActionKeywordModels() new(Settings.ActionKeyword.IndexSearchActionKeyword, "plugin_explorer_actionkeywordview_indexsearch"), new(Settings.ActionKeyword.QuickAccessActionKeyword, - "plugin_explorer_actionkeywordview_quickaccess") + "plugin_explorer_actionkeywordview_quickaccess"), + new(Settings.ActionKeyword.FolderSearchActionKeyword, + "plugin_explorer_actionkeywordview_foldersearch"), + new(Settings.ActionKeyword.FileSearchActionKeyword, + "plugin_explorer_actionkeywordview_filesearch") }; } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml index 79000e70055..40aaf2bf07d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml @@ -674,17 +674,27 @@ + - + + @@ -695,7 +705,7 @@