Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
<system:String x:Key="plugin_explorer_actionkeywordview_filecontentsearch">File Content Search:</system:String>
<system:String x:Key="plugin_explorer_actionkeywordview_indexsearch">Index Search:</system:String>
<system:String x:Key="plugin_explorer_actionkeywordview_quickaccess">Quick Access:</system:String>
<system:String x:Key="plugin_explorer_actionkeywordview_foldersearch">Folder Search:</system:String>
<system:String x:Key="plugin_explorer_actionkeywordview_filesearch">File Search:</system:String>
<system:String x:Key="plugin_explorer_actionkeyword_current">Current Action Keyword</system:String>
<system:String x:Key="plugin_explorer_actionkeyword_done">Done</system:String>
<system:String x:Key="plugin_explorer_actionkeyword_enabled">Enabled</system:String>
Expand All @@ -74,7 +76,7 @@
<system:String x:Key="plugin_explorer_Directory_Recursive_Search_Engine">Directory Recursive Search Engine</system:String>
<system:String x:Key="plugin_explorer_Index_Search_Engine">Index Search Engine</system:String>
<system:String x:Key="plugin_explorer_Open_Window_Index_Option">Open Windows Index Option</system:String>
<system:String x:Key="plugin_explorer_Excluded_File_Types">Excluded File Types (comma seperated)</system:String>

Check warning on line 79 in Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`seperated` is not a recognized word. (unrecognized-spelling)
<system:String x:Key="plugin_explorer_Excluded_File_Types_Tooltip">For example: exe,jpg,png</system:String>
<system:String x:Key="plugin_explorer_Maximum_Results">Maximum results</system:String>
<system:String x:Key="plugin_explorer_Maximum_Results_Tooltip">The maximum number of results requested from active search engine</system:String>
Expand Down
131 changes: 68 additions & 63 deletions Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -24,6 +27,7 @@
Settings = settings;
}


/// <summary>
/// Note: A path that ends with "\" and one that doesn't will not be regarded as equal.
/// </summary>
Expand All @@ -47,79 +51,72 @@
internal async Task<List<Result>> SearchAsync(Query query, CancellationToken token)
{
var results = new HashSet<Result>(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();
Comment on lines +63 to +64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Filter Quick Access results for type-specific keywords

Folder/File action keywords still surface Quick Access items of the opposite type because MergeQuickAccessInResultsIfQueryMatch doesn’t respect the active keyword. For example, add a Quick Access link to a .txt file and run the new folder-only keyword—its result still appears, defeating the filter. Extend the merge helper to accept the active keyword and drop mismatched Quick Access entries so the new feature behaves correctly.

-                MergeQuickAccessInResultsIfQueryMatch(results, query);
+                MergeQuickAccessInResultsIfQueryMatch(results, query, null);
@@
-            MergeQuickAccessInResultsIfQueryMatch(results, query);
+            MergeQuickAccessInResultsIfQueryMatch(results, query, activeActionKeyword);
@@
-        private void MergeQuickAccessInResultsIfQueryMatch(HashSet<Result> results, Query query)
+        private void MergeQuickAccessInResultsIfQueryMatch(HashSet<Result> results, Query query, ActionKeyword? activeActionKeyword)
         {
-            var quickAccessMatched = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks);
-            if (quickAccessMatched != null && quickAccessMatched.Any()) results.UnionWith(quickAccessMatched);
+            var quickAccessMatched = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks);
+            if (quickAccessMatched == null || quickAccessMatched.Count == 0)
+            {
+                return;
+            }
+
+            if (activeActionKeyword is ActionKeyword.FolderSearchActionKeyword)
+            {
+                quickAccessMatched = quickAccessMatched
+                    .Where(r => r.ResultType is ResultType.Folder or ResultType.Volume)
+                    .ToList();
+            }
+            else if (activeActionKeyword is ActionKeyword.FileSearchActionKeyword)
+            {
+                quickAccessMatched = quickAccessMatched
+                    .Where(r => r.ResultType is ResultType.File)
+                    .ToList();
+            }
+
+            if (quickAccessMatched.Count > 0)
+            {
+                results.UnionWith(quickAccessMatched);
+            }
         }

Also applies to: 107-109, 282-286

🤖 Prompt for AI Agents
In Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs around lines
63-64 (also update callers at 107-109 and 282-286), the
MergeQuickAccessInResultsIfQueryMatch helper is merging Quick Access entries
without considering the active keyword type, so folder-only or file-only
keywords still show opposite-type Quick Access items; change the helper
signature to accept the active keyword (or an enum/flag indicating folder vs
file) and, before merging, filter out Quick Access entries whose target type
does not match the active keyword, then update every call site (lines 63-64,
107-109, 282-286) to pass the current active keyword/flag so mismatched Quick
Access items are dropped during the merge.

}
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<Result>();
return QuickAccess.AccessLinkListAll(query, Settings.QuickAccessLinks);
}

IAsyncEnumerable<SearchResult> 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);

searchResults = Settings.ContentIndexProvider.ContentSearchAsync("", query.Search, token);
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)
{
Expand All @@ -140,25 +137,6 @@
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<Result> EverythingContentSearchResult(Query query)
{
Expand Down Expand Up @@ -188,7 +166,7 @@
if (EnvironmentVariables.IsEnvironmentVariableSearch(querySearch))
return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, Context);

// Query is a location path with a full environment variable, eg. %appdata%\somefolder\, c:\users\%USERNAME%\downloads

Check warning on line 169 in Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`somefolder` is not a recognized word. (unrecognized-spelling)
var needToExpand = EnvironmentVariables.HasEnvironmentVar(querySearch);
var path = needToExpand ? Environment.ExpandEnvironmentVariables(querySearch) : querySearch;

Expand Down Expand Up @@ -280,5 +258,32 @@

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<Result> results, Query query)
{
var quickAccessMatched = QuickAccess.AccessLinkListMatched(query, Settings.QuickAccessLinks);
if (quickAccessMatched != null && quickAccessMatched.Any()) results.UnionWith(quickAccessMatched);
}
}

}
49 changes: 42 additions & 7 deletions Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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")
};

Expand All @@ -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")
};

Expand All @@ -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")
};

Expand All @@ -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;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,15 +271,19 @@
ActionKeywordsModels = new List<ActionKeywordModel>
{
new(Settings.ActionKeyword.SearchActionKeyword,
"plugin_explorer_actionkeywordview_search"),

Check warning on line 274 in Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`actionkeywordview` is not a recognized word. (unrecognized-spelling)
new(Settings.ActionKeyword.FileContentSearchActionKeyword,
"plugin_explorer_actionkeywordview_filecontentsearch"),

Check warning on line 276 in Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`filecontentsearch` is not a recognized word. (unrecognized-spelling)

Check warning on line 276 in Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`actionkeywordview` is not a recognized word. (unrecognized-spelling)
new(Settings.ActionKeyword.PathSearchActionKeyword,
"plugin_explorer_actionkeywordview_pathsearch"),

Check warning on line 278 in Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`actionkeywordview` is not a recognized word. (unrecognized-spelling)
new(Settings.ActionKeyword.IndexSearchActionKeyword,
"plugin_explorer_actionkeywordview_indexsearch"),

Check warning on line 280 in Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`actionkeywordview` is not a recognized word. (unrecognized-spelling)
new(Settings.ActionKeyword.QuickAccessActionKeyword,
"plugin_explorer_actionkeywordview_quickaccess")
"plugin_explorer_actionkeywordview_quickaccess"),

Check warning on line 282 in Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs

View workflow job for this annotation

GitHub Actions / Check Spelling

`actionkeywordview` is not a recognized word. (unrecognized-spelling)
new(Settings.ActionKeyword.FolderSearchActionKeyword,
"plugin_explorer_actionkeywordview_foldersearch"),
new(Settings.ActionKeyword.FileSearchActionKeyword,
"plugin_explorer_actionkeywordview_filesearch")
};
}

Expand Down Expand Up @@ -465,7 +469,7 @@

private static string? PromptUserSelectPath(ResultType type, string? initialDirectory = null)
{
string? path = null;
string? path = null;

if (type is ResultType.Folder)
{
Expand Down
Loading