diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index c40040df5be..45c0d72acd9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -58,6 +58,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..463d39be1c3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,13 +1,16 @@ -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; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Windows; 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 @@ -24,6 +27,7 @@ public SearchManager(Settings settings, PluginInitContext context) Settings = settings; } + /// /// Note: A path that ends with "\" and one that doesn't will not be regarded as equal. /// @@ -47,45 +51,41 @@ public int GetHashCode(Result obj) internal async Task> SearchAsync(Query query, CancellationToken token) { var results = new HashSet(PathEqualityComparator.Instance); + var keywordStr = query.ActionKeyword.Length == 0 ? Query.GlobalPluginWildcardSign : query.ActionKeyword; + bool 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)) + var activeActionKeyword = Settings.GetActiveActionKeyword(keywordStr); + + if (activeActionKeyword == null && !isPathSearch) { - if (string.IsNullOrEmpty(query.Search) && ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword)) - return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks); + MergeQuickAccessInResultsIfQueryMatch(results, query); + return results.ToList(); } - else + + 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(); - 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); @@ -93,33 +93,30 @@ when ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKey engineName = Enum.GetName(Settings.ContentSearchEngine); break; - case false - when ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) - || ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword): + case false + 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); 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) { @@ -140,25 +137,6 @@ when ActionKeywordMatch(query, Settings.ActionKeyword.QuickAccessActionKeyword): 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") - }; - } private List EverythingContentSearchResult(Query query) { @@ -280,5 +258,32 @@ private bool IsExcludedFile(SearchResult result) return excludedFileTypes.Contains(fileExtension, StringComparer.OrdinalIgnoreCase); } + + private bool ShouldSkip(ActionKeyword actionKeywordActive, SearchResult search) + { + if (search.Type == ResultType.File && IsExcludedFile(search)) + return true; + + if (actionKeywordActive.Equals(ActionKeyword.FolderSearchActionKeyword) + && search.Type != ResultType.Folder) + { + return true; + } + + if (actionKeywordActive.Equals(ActionKeyword.FileSearchActionKeyword) + && search.Type != ResultType.File) + { + return true; + } + + return false; + } + + private void MergeQuickAccessInResultsIfQueryMatch(HashSet results, Query query) + { + var quickAccessMatched = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks); + if (quickAccessMatched != null && quickAccessMatched.Any()) results.UnionWith(quickAccessMatched); + } } + } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index 8d62531cd62..0b02c374bc5 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,15 @@ 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 WarnWindowsSearchServiceOff { get; set; } = true; public bool ShowFileSizeInPreviewPanel { get; set; } = true; @@ -154,13 +164,16 @@ 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 +183,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 +195,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 +207,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 +219,23 @@ 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 (ActionKeyword action in Enum.GetValues(typeof(ActionKeyword))) + { + 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..c4ca3f85310 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") }; } @@ -465,7 +469,7 @@ private void ShowUnselectedMessage() private static string? PromptUserSelectPath(ResultType type, string? initialDirectory = null) { - string? path = null; + string? path = null; if (type is ResultType.Folder) {