Implement Path Enumeration and ContentSearch

This commit is contained in:
Hongtao Zhang 2022-05-15 15:15:56 -05:00
parent fa0cd35e18
commit 9b471de3cf
No known key found for this signature in database
GPG key ID: 75F655B91C7AC9BB
9 changed files with 147 additions and 55 deletions

View file

@ -520,7 +520,7 @@ namespace Flow.Launcher
&& QueryTextBox.CaretIndex == QueryTextBox.Text.Length)
{
var queryWithoutActionKeyword =
QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins).Search;
QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search;
if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword))
{

View file

@ -10,7 +10,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
{
public static class DirectoryInfoSearch
{
internal static List<Result> TopLevelDirectorySearch(Query query, string search, CancellationToken token)
internal static IEnumerable<SearchResult> TopLevelDirectorySearch(Query query, string search, CancellationToken token)
{
var criteria = ConstructSearchCriteria(search);
@ -44,31 +44,36 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
return incompleteName;
}
private static List<Result> DirectorySearch(EnumerationOptions enumerationOption, Query query, string search,
private static IEnumerable<SearchResult> DirectorySearch(EnumerationOptions enumerationOption, Query query, string search,
string searchCriteria, CancellationToken token)
{
var results = new List<Result>();
var results = new List<SearchResult>();
var path = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(search);
var folderList = new List<Result>();
var fileList = new List<Result>();
try
{
var directoryInfo = new System.IO.DirectoryInfo(path);
foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption)
)
foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption))
{
if (fileSystemInfo is System.IO.DirectoryInfo)
{
folderList.Add(ResultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName,
fileSystemInfo.FullName, query, 0, true, false));
results.Add(new SearchResult()
{
FullPath = fileSystemInfo.FullName,
Type = ResultType.Folder,
WindowsIndexed = false
});
}
else
{
fileList.Add(ResultManager.CreateFileResult(fileSystemInfo.FullName, query, 0, true, false));
results.Add(new SearchResult()
{
FullPath = fileSystemInfo.FullName,
Type = ResultType.File,
WindowsIndexed = false
});
}
token.ThrowIfCancellationRequested();
@ -77,13 +82,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
catch (Exception e)
{
Log.Exception("Flow.Plugin.Explorer.", nameof(DirectoryInfoSearch), e);
results.Add(new Result {Title = e.Message, Score = 501});
return results;
throw;
}
// Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList();
return results.OrderBy(r=>r.Type).ThenBy(r=>r.FullPath);
}
}
}

View file

@ -107,38 +107,38 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
/// <param name="offset">The offset.</param>
/// <param name="maxCount">The max count.</param>
/// <returns></returns>
public static IEnumerable<SearchResult> SearchAsync(string keyword,
CancellationToken token,
SortOption sortOption = SortOption.NAME_ASCENDING,
string parentPath = "",
bool recursive = false,
int offset = 0,
int maxCount = 100)
public static IEnumerable<SearchResult> SearchAsync(EverythingSearchOption option,
CancellationToken token)
{
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset));
if (option.Offset < 0)
throw new ArgumentOutOfRangeException(nameof(option.Offset), option.Offset, "Offset must be greater than or equal to 0");
if (maxCount < 0)
throw new ArgumentOutOfRangeException(nameof(maxCount));
if (option.MaxCount < 0)
throw new ArgumentOutOfRangeException(nameof(option.MaxCount), option.MaxCount, "MaxCount must be greater than or equal to 0");
lock (syncObject)
{
if (keyword.StartsWith("@"))
if (option.Keyword.StartsWith("@"))
{
EverythingApiDllImport.Everything_SetRegex(true);
keyword = keyword[1..];
option.Keyword = option.Keyword[1..];
}
if (!string.IsNullOrEmpty(parentPath))
if (!string.IsNullOrEmpty(option.ParentPath))
{
keyword += $" {(recursive ? "" : "parent:")}\"{parentPath}\"";
option.Keyword += $" {(option.IsRecursive ? "" : "parent:")}\"{option.ParentPath}\"";
}
EverythingApiDllImport.Everything_SetSearchW(keyword);
EverythingApiDllImport.Everything_SetOffset(offset);
EverythingApiDllImport.Everything_SetMax(maxCount);
if (option.IsContentSearch)
{
option.Keyword += $" content:\"{option.ContentSearchKeyword}\"";
}
EverythingApiDllImport.Everything_SetSort(sortOption);
EverythingApiDllImport.Everything_SetSearchW(option.Keyword);
EverythingApiDllImport.Everything_SetOffset(option.Offset);
EverythingApiDllImport.Everything_SetMax(option.MaxCount);
EverythingApiDllImport.Everything_SetSort(option.SortOption);
if (token.IsCancellationRequested)
{

View file

@ -8,7 +8,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
public class EverythingSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
{
private Settings Settings { get; }
public EverythingSearchManager(Settings settings)
{
Settings = settings;
@ -17,16 +17,35 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
public ValueTask<IEnumerable<SearchResult>> SearchAsync(string search, CancellationToken token)
{
return ValueTask.FromResult(EverythingApi.SearchAsync(search, token, Settings.SortOption));
return ValueTask.FromResult(EverythingApi.SearchAsync(
new EverythingSearchOption(search, Settings.SortOption),
token));
}
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token)
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string plainSearch,
string contentSearch, CancellationToken token)
{
return new ValueTask<IEnumerable<SearchResult>>(new List<SearchResult>());
if (!Settings.EnableEverythingContentSearch)
{
return new ValueTask<IEnumerable<SearchResult>>(new List<SearchResult>());
}
return new ValueTask<IEnumerable<SearchResult>>(EverythingApi.SearchAsync(
new EverythingSearchOption(
plainSearch,
Settings.SortOption,
true,
contentSearch),
token));
}
public ValueTask<IEnumerable<SearchResult>> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
{
return new ValueTask<IEnumerable<SearchResult>>(
EverythingApi.SearchAsync("", token, Settings.SortOption, path, recursive));
EverythingApi.SearchAsync(
new EverythingSearchOption(search,
Settings.SortOption,
parentPath: path,
isRecursive: recursive),
token));
}
}
}

View file

@ -0,0 +1,35 @@
using Flow.Launcher.Plugin.Everything.Everything;
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{
public struct EverythingSearchOption
{
public EverythingSearchOption(string keyword,
SortOption sortOption,
bool isContentSearch = false,
string contentSearchKeyword = "",
string parentPath = "",
bool isRecursive = true,
int offset = 0,
int maxCount = 100)
{
Keyword = keyword;
SortOption = sortOption;
ContentSearchKeyword = contentSearchKeyword;
IsContentSearch = isContentSearch;
ParentPath = parentPath;
IsRecursive = isRecursive;
Offset = offset;
MaxCount = maxCount;
}
public string Keyword { get; set; }
public SortOption SortOption { get; set; }
public string ParentPath { get; set; }
public bool IsRecursive { get; set; }
public bool IsContentSearch { get; set; }
public string ContentSearchKeyword { get; set; }
public int Offset { get;set; }
public int MaxCount { get; set; }
}
}

View file

@ -6,6 +6,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
public interface IContentIndexProvider
{
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token);
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token);
}
}

View file

@ -1,4 +1,5 @@
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.Explorer.Search.WindowsIndex;
using Flow.Launcher.Plugin.SharedCommands;
@ -59,9 +60,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search
IEnumerable<SearchResult> searchResults;
if (IsFileContentSearch(query.ActionKeyword))
if (ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword))
{
searchResults = await Settings.ContentIndexProvider.ContentSearchAsync(query.Search, token);
if (Settings.ContentIndexProvider is EverythingSearchManager && !Settings.EnableEverythingContentSearch)
{
return EverythingContentSearchResult(query);
}
searchResults = await Settings.ContentIndexProvider.ContentSearchAsync("", query.Search, token);
}
else
{
@ -74,9 +79,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search
results.UnionWith(await PathSearchAsync(query, token).ConfigureAwait(false));
}
if ((ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) ||
ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword)) &&
querySearch.Length > 0 &&
if (((ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) ||
ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword)) &&
querySearch.Length > 0) ||
ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword) &&
!querySearch.IsLocationPathString())
{
if (searchResults != null)
@ -101,7 +107,27 @@ namespace Flow.Launcher.Plugin.Explorer.Search
Settings.ActionKeyword.IndexSearchActionKeyword => Settings.IndexSearchKeywordEnabled &&
keyword == Settings.IndexSearchActionKeyword,
Settings.ActionKeyword.QuickAccessActionKeyword => Settings.QuickAccessKeywordEnabled &&
keyword == Settings.QuickAccessActionKeyword
keyword == Settings.QuickAccessActionKeyword,
_ => throw new ArgumentOutOfRangeException(nameof(allowedActionKeyword), allowedActionKeyword, "actionKeyword out of range")
};
}
private static List<Result> EverythingContentSearchResult(Query query)
{
return new List<Result>()
{
new()
{
Title = "Do you want to enable content search for Everything?",
SubTitle = "It can be super slow without index (which is only supported in Everything 1.5+)",
IcoPath = "Images/search.png",
Action = c =>
{
Settings.EnableEverythingContentSearch = true;
Context.API.ChangeQuery(query.RawQuery, true);
return false;
}
}
};
}
@ -120,7 +146,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
var isEnvironmentVariablePath = querySearch[1..].Contains("%\\");
var locationPath = querySearch;
if (isEnvironmentVariablePath)
locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath);
@ -136,18 +162,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search
IEnumerable<SearchResult> directoryResult;
if (query.Search.Contains('>'))
var recursiveIndicatorIndex = query.Search.IndexOf('>');
if (recursiveIndicatorIndex > 0 && Settings.PathEnumerationEngine != Settings.PathTraversalEngineOption.Direct)
{
directoryResult =
await Settings.PathEnumerator.EnumerateAsync(locationPath, "", false, token)
await Settings.PathEnumerator.EnumerateAsync(query.Search[..recursiveIndicatorIndex],
query.Search[(recursiveIndicatorIndex + 1)..],
true,
token)
.ConfigureAwait(false);
}
else
{
directoryResult = DirectoryInfoSearch.TopLevelDirectorySearch(query, query.Search, token);
}
token.ThrowIfCancellationRequested();

View file

@ -59,9 +59,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
return await WindowsIndexFilesAndFoldersSearchAsync(search, token);
}
public async ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token)
public async ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
{
return await WindowsIndexFileContentSearchAsync(search, token);
return await WindowsIndexFileContentSearchAsync(contentSearch, token);
}
public async ValueTask<IEnumerable<SearchResult>> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
{

View file

@ -67,7 +67,8 @@ namespace Flow.Launcher.Plugin.Explorer
_fileContentIndexProviders = new List<IContentIndexProvider>
{
windowsIndexManager, everythingManager
windowsIndexManager,
everythingManager,
};
}
@ -111,6 +112,8 @@ namespace Flow.Launcher.Plugin.Explorer
public SortOption[] SortOptions { get; set; } = Enum.GetValues<SortOption>();
public SortOption SortOption { get; set; } = SortOption.NAME_ASCENDING;
public bool EnableEverythingContentSearch { get; set; } = false;
#endregion