mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Implement Path Enumeration and ContentSearch
This commit is contained in:
parent
fa0cd35e18
commit
9b471de3cf
9 changed files with 147 additions and 55 deletions
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue