Skip to content

Commit 9b471de

Browse files
committed
Implement Path Enumeration and ContentSearch
1 parent fa0cd35 commit 9b471de

File tree

9 files changed

+147
-55
lines changed

9 files changed

+147
-55
lines changed

Flow.Launcher/MainWindow.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ private void OnKeyDown(object sender, KeyEventArgs e)
520520
&& QueryTextBox.CaretIndex == QueryTextBox.Text.Length)
521521
{
522522
var queryWithoutActionKeyword =
523-
QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins).Search;
523+
QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search;
524524

525525
if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword))
526526
{

Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
1010
{
1111
public static class DirectoryInfoSearch
1212
{
13-
internal static List<Result> TopLevelDirectorySearch(Query query, string search, CancellationToken token)
13+
internal static IEnumerable<SearchResult> TopLevelDirectorySearch(Query query, string search, CancellationToken token)
1414
{
1515
var criteria = ConstructSearchCriteria(search);
1616

@@ -44,31 +44,36 @@ public static string ConstructSearchCriteria(string search)
4444
return incompleteName;
4545
}
4646

47-
private static List<Result> DirectorySearch(EnumerationOptions enumerationOption, Query query, string search,
47+
private static IEnumerable<SearchResult> DirectorySearch(EnumerationOptions enumerationOption, Query query, string search,
4848
string searchCriteria, CancellationToken token)
4949
{
50-
var results = new List<Result>();
50+
var results = new List<SearchResult>();
5151

5252
var path = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(search);
5353

54-
var folderList = new List<Result>();
55-
var fileList = new List<Result>();
56-
5754
try
5855
{
5956
var directoryInfo = new System.IO.DirectoryInfo(path);
6057

61-
foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption)
62-
)
58+
foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption))
6359
{
6460
if (fileSystemInfo is System.IO.DirectoryInfo)
6561
{
66-
folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName,
67-
fileSystemInfo.FullName, query, 0, true, false));
62+
results.Add(new SearchResult()
63+
{
64+
FullPath = fileSystemInfo.FullName,
65+
Type = ResultType.Folder,
66+
WindowsIndexed = false
67+
});
6868
}
6969
else
7070
{
71-
fileList.Add(ResultManager.CreateFileResult(fileSystemInfo.FullName, query, 0, true, false));
71+
results.Add(new SearchResult()
72+
{
73+
FullPath = fileSystemInfo.FullName,
74+
Type = ResultType.File,
75+
WindowsIndexed = false
76+
});
7277
}
7378

7479
token.ThrowIfCancellationRequested();
@@ -77,13 +82,11 @@ private static List<Result> DirectorySearch(EnumerationOptions enumerationOption
7782
catch (Exception e)
7883
{
7984
Log.Exception("Flow.Plugin.Explorer.", nameof(DirectoryInfoSearch), e);
80-
results.Add(new Result {Title = e.Message, Score = 501});
81-
82-
return results;
85+
throw;
8386
}
8487

8588
// Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
86-
return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList();
89+
return results.OrderBy(r=>r.Type).ThenBy(r=>r.FullPath);
8790
}
8891
}
8992
}

Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,38 +107,38 @@ public static bool IsFastSortOption(SortOption sortOption)
107107
/// <param name="offset">The offset.</param>
108108
/// <param name="maxCount">The max count.</param>
109109
/// <returns></returns>
110-
public static IEnumerable<SearchResult> SearchAsync(string keyword,
111-
CancellationToken token,
112-
SortOption sortOption = SortOption.NAME_ASCENDING,
113-
string parentPath = "",
114-
bool recursive = false,
115-
int offset = 0,
116-
int maxCount = 100)
110+
public static IEnumerable<SearchResult> SearchAsync(EverythingSearchOption option,
111+
CancellationToken token)
117112
{
118-
if (offset < 0)
119-
throw new ArgumentOutOfRangeException(nameof(offset));
113+
if (option.Offset < 0)
114+
throw new ArgumentOutOfRangeException(nameof(option.Offset), option.Offset, "Offset must be greater than or equal to 0");
120115

121-
if (maxCount < 0)
122-
throw new ArgumentOutOfRangeException(nameof(maxCount));
116+
if (option.MaxCount < 0)
117+
throw new ArgumentOutOfRangeException(nameof(option.MaxCount), option.MaxCount, "MaxCount must be greater than or equal to 0");
123118

124119
lock (syncObject)
125120
{
126-
if (keyword.StartsWith("@"))
121+
if (option.Keyword.StartsWith("@"))
127122
{
128123
EverythingApiDllImport.Everything_SetRegex(true);
129-
keyword = keyword[1..];
124+
option.Keyword = option.Keyword[1..];
130125
}
131126

132-
if (!string.IsNullOrEmpty(parentPath))
127+
if (!string.IsNullOrEmpty(option.ParentPath))
133128
{
134-
keyword += $" {(recursive ? "" : "parent:")}\"{parentPath}\"";
129+
option.Keyword += $" {(option.IsRecursive ? "" : "parent:")}\"{option.ParentPath}\"";
135130
}
136131

137-
EverythingApiDllImport.Everything_SetSearchW(keyword);
138-
EverythingApiDllImport.Everything_SetOffset(offset);
139-
EverythingApiDllImport.Everything_SetMax(maxCount);
132+
if (option.IsContentSearch)
133+
{
134+
option.Keyword += $" content:\"{option.ContentSearchKeyword}\"";
135+
}
136+
137+
EverythingApiDllImport.Everything_SetSearchW(option.Keyword);
138+
EverythingApiDllImport.Everything_SetOffset(option.Offset);
139+
EverythingApiDllImport.Everything_SetMax(option.MaxCount);
140140

141-
EverythingApiDllImport.Everything_SetSort(sortOption);
141+
EverythingApiDllImport.Everything_SetSort(option.SortOption);
142142

143143
if (token.IsCancellationRequested)
144144
{

Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
88
public class EverythingSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
99
{
1010
private Settings Settings { get; }
11-
11+
1212
public EverythingSearchManager(Settings settings)
1313
{
1414
Settings = settings;
@@ -17,16 +17,35 @@ public EverythingSearchManager(Settings settings)
1717

1818
public ValueTask<IEnumerable<SearchResult>> SearchAsync(string search, CancellationToken token)
1919
{
20-
return ValueTask.FromResult(EverythingApi.SearchAsync(search, token, Settings.SortOption));
20+
return ValueTask.FromResult(EverythingApi.SearchAsync(
21+
new EverythingSearchOption(search, Settings.SortOption),
22+
token));
2123
}
22-
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token)
24+
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string plainSearch,
25+
string contentSearch, CancellationToken token)
2326
{
24-
return new ValueTask<IEnumerable<SearchResult>>(new List<SearchResult>());
27+
if (!Settings.EnableEverythingContentSearch)
28+
{
29+
return new ValueTask<IEnumerable<SearchResult>>(new List<SearchResult>());
30+
}
31+
32+
return new ValueTask<IEnumerable<SearchResult>>(EverythingApi.SearchAsync(
33+
new EverythingSearchOption(
34+
plainSearch,
35+
Settings.SortOption,
36+
true,
37+
contentSearch),
38+
token));
2539
}
2640
public ValueTask<IEnumerable<SearchResult>> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
2741
{
2842
return new ValueTask<IEnumerable<SearchResult>>(
29-
EverythingApi.SearchAsync("", token, Settings.SortOption, path, recursive));
43+
EverythingApi.SearchAsync(
44+
new EverythingSearchOption(search,
45+
Settings.SortOption,
46+
parentPath: path,
47+
isRecursive: recursive),
48+
token));
3049
}
3150
}
3251
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Flow.Launcher.Plugin.Everything.Everything;
2+
3+
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
4+
{
5+
public struct EverythingSearchOption
6+
{
7+
public EverythingSearchOption(string keyword,
8+
SortOption sortOption,
9+
bool isContentSearch = false,
10+
string contentSearchKeyword = "",
11+
string parentPath = "",
12+
bool isRecursive = true,
13+
int offset = 0,
14+
int maxCount = 100)
15+
{
16+
Keyword = keyword;
17+
SortOption = sortOption;
18+
ContentSearchKeyword = contentSearchKeyword;
19+
IsContentSearch = isContentSearch;
20+
ParentPath = parentPath;
21+
IsRecursive = isRecursive;
22+
Offset = offset;
23+
MaxCount = maxCount;
24+
}
25+
public string Keyword { get; set; }
26+
public SortOption SortOption { get; set; }
27+
public string ParentPath { get; set; }
28+
public bool IsRecursive { get; set; }
29+
30+
public bool IsContentSearch { get; set; }
31+
public string ContentSearchKeyword { get; set; }
32+
public int Offset { get;set; }
33+
public int MaxCount { get; set; }
34+
}
35+
}

Plugins/Flow.Launcher.Plugin.Explorer/Search/IContentIndexProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
66
{
77
public interface IContentIndexProvider
88
{
9-
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token);
9+
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token);
1010
}
1111
}

Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo;
2+
using Flow.Launcher.Plugin.Explorer.Search.Everything;
23
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
34
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
45
using Flow.Launcher.Plugin.SharedCommands;
@@ -59,9 +60,13 @@ internal async Task<List<Result>> SearchAsync(Query query, CancellationToken tok
5960

6061
IEnumerable<SearchResult> searchResults;
6162

62-
if (IsFileContentSearch(query.ActionKeyword))
63+
if (ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword))
6364
{
64-
searchResults = await Settings.ContentIndexProvider.ContentSearchAsync(query.Search, token);
65+
if (Settings.ContentIndexProvider is EverythingSearchManager && !Settings.EnableEverythingContentSearch)
66+
{
67+
return EverythingContentSearchResult(query);
68+
}
69+
searchResults = await Settings.ContentIndexProvider.ContentSearchAsync("", query.Search, token);
6570
}
6671
else
6772
{
@@ -74,9 +79,10 @@ internal async Task<List<Result>> SearchAsync(Query query, CancellationToken tok
7479
results.UnionWith(await PathSearchAsync(query, token).ConfigureAwait(false));
7580
}
7681

77-
if ((ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) ||
78-
ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword)) &&
79-
querySearch.Length > 0 &&
82+
if (((ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) ||
83+
ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword)) &&
84+
querySearch.Length > 0) ||
85+
ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword) &&
8086
!querySearch.IsLocationPathString())
8187
{
8288
if (searchResults != null)
@@ -101,7 +107,27 @@ private bool ActionKeywordMatch(Query query, Settings.ActionKeyword allowedActio
101107
Settings.ActionKeyword.IndexSearchActionKeyword => Settings.IndexSearchKeywordEnabled &&
102108
keyword == Settings.IndexSearchActionKeyword,
103109
Settings.ActionKeyword.QuickAccessActionKeyword => Settings.QuickAccessKeywordEnabled &&
104-
keyword == Settings.QuickAccessActionKeyword
110+
keyword == Settings.QuickAccessActionKeyword,
111+
_ => throw new ArgumentOutOfRangeException(nameof(allowedActionKeyword), allowedActionKeyword, "actionKeyword out of range")
112+
};
113+
}
114+
115+
private static List<Result> EverythingContentSearchResult(Query query)
116+
{
117+
return new List<Result>()
118+
{
119+
new()
120+
{
121+
Title = "Do you want to enable content search for Everything?",
122+
SubTitle = "It can be super slow without index (which is only supported in Everything 1.5+)",
123+
IcoPath = "Images/search.png",
124+
Action = c =>
125+
{
126+
Settings.EnableEverythingContentSearch = true;
127+
Context.API.ChangeQuery(query.RawQuery, true);
128+
return false;
129+
}
130+
}
105131
};
106132
}
107133

@@ -120,7 +146,7 @@ public async Task<List<Result>> PathSearchAsync(Query query, CancellationToken t
120146
var isEnvironmentVariablePath = querySearch[1..].Contains("%\\");
121147

122148
var locationPath = querySearch;
123-
149+
124150
if (isEnvironmentVariablePath)
125151
locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath);
126152

@@ -136,18 +162,24 @@ public async Task<List<Result>> PathSearchAsync(Query query, CancellationToken t
136162

137163
IEnumerable<SearchResult> directoryResult;
138164

139-
if (query.Search.Contains('>'))
165+
var recursiveIndicatorIndex = query.Search.IndexOf('>');
166+
167+
if (recursiveIndicatorIndex > 0 && Settings.PathEnumerationEngine != Settings.PathTraversalEngineOption.Direct)
140168
{
141169
directoryResult =
142-
await Settings.PathEnumerator.EnumerateAsync(locationPath, "", false, token)
170+
await Settings.PathEnumerator.EnumerateAsync(query.Search[..recursiveIndicatorIndex],
171+
query.Search[(recursiveIndicatorIndex + 1)..],
172+
true,
173+
token)
143174
.ConfigureAwait(false);
175+
144176
}
145177
else
146178
{
147179
directoryResult = DirectoryInfoSearch.TopLevelDirectorySearch(query, query.Search, token);
148180
}
149-
150-
181+
182+
151183

152184
token.ThrowIfCancellationRequested();
153185

Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ public async ValueTask<IEnumerable<SearchResult>> SearchAsync(string search, Can
5959
{
6060
return await WindowsIndexFilesAndFoldersSearchAsync(search, token);
6161
}
62-
public async ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token)
62+
public async ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
6363
{
64-
return await WindowsIndexFileContentSearchAsync(search, token);
64+
return await WindowsIndexFileContentSearchAsync(contentSearch, token);
6565
}
6666
public async ValueTask<IEnumerable<SearchResult>> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
6767
{

Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ public Settings()
6767

6868
_fileContentIndexProviders = new List<IContentIndexProvider>
6969
{
70-
windowsIndexManager, everythingManager
70+
windowsIndexManager,
71+
everythingManager,
7172
};
7273
}
7374

@@ -111,6 +112,8 @@ public enum ContentIndexSearchEngineOption
111112
public SortOption[] SortOptions { get; set; } = Enum.GetValues<SortOption>();
112113

113114
public SortOption SortOption { get; set; } = SortOption.NAME_ASCENDING;
115+
116+
public bool EnableEverythingContentSearch { get; set; } = false;
114117

115118
#endregion
116119

0 commit comments

Comments
 (0)