Support Path Enumeration (Part 1)

This commit is contained in:
Hongtao Zhang 2022-03-28 15:28:18 -05:00
parent b671f562ef
commit fa0cd35e18
No known key found for this signature in database
GPG key ID: 75F655B91C7AC9BB
10 changed files with 129 additions and 85 deletions

View file

@ -101,15 +101,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
/// </summary> /// </summary>
/// <param name="keyword">The key word.</param> /// <param name="keyword">The key word.</param>
/// <param name="token">when cancelled the current search will stop and exit (and would not reset)</param> /// <param name="token">when cancelled the current search will stop and exit (and would not reset)</param>
/// <param name="parentPath">Search Within a parent folder</param>
/// <param name="recursive">Search Within sub folder of the parent folder</param>
/// <param name="sortOption">Sort By</param> /// <param name="sortOption">Sort By</param>
/// <param name="offset">The offset.</param> /// <param name="offset">The offset.</param>
/// <param name="maxCount">The max count.</param> /// <param name="maxCount">The max count.</param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<SearchResult> SearchAsync(string keyword, CancellationToken token, SortOption sortOption = SortOption.NAME_ASCENDING, int offset = 0, int maxCount = 100) 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)
{ {
if (string.IsNullOrEmpty(keyword))
throw new ArgumentNullException(nameof(keyword));
if (offset < 0) if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset)); throw new ArgumentOutOfRangeException(nameof(offset));
@ -124,6 +129,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
keyword = keyword[1..]; keyword = keyword[1..];
} }
if (!string.IsNullOrEmpty(parentPath))
{
keyword += $" {(recursive ? "" : "parent:")}\"{parentPath}\"";
}
EverythingApiDllImport.Everything_SetSearchW(keyword); EverythingApiDllImport.Everything_SetSearchW(keyword);
EverythingApiDllImport.Everything_SetOffset(offset); EverythingApiDllImport.Everything_SetOffset(offset);
EverythingApiDllImport.Everything_SetMax(maxCount); EverythingApiDllImport.Everything_SetMax(maxCount);

View file

@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.Everything namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{ {
public class EverythingSearchManager : IIndexProvider public class EverythingSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
{ {
private Settings Settings { get; } private Settings Settings { get; }
@ -15,13 +15,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
} }
public ValueTask<IEnumerable<SearchResult>> SearchAsync(Query query, CancellationToken token) public ValueTask<IEnumerable<SearchResult>> SearchAsync(string search, CancellationToken token)
{ {
return ValueTask.FromResult(EverythingApi.SearchAsync(query.Search, token, Settings.SortOption)); return ValueTask.FromResult(EverythingApi.SearchAsync(search, token, Settings.SortOption));
} }
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(Query query, CancellationToken token) public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token)
{ {
return new ValueTask<IEnumerable<SearchResult>>(new List<SearchResult>()); return new ValueTask<IEnumerable<SearchResult>>(new List<SearchResult>());
} }
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));
}
} }
} }

View file

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search
{
public interface IContentIndexProvider
{
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token);
}
}

View file

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search
{
public interface IIndexProvider
{
public ValueTask<IEnumerable<SearchResult>> SearchAsync(string search, CancellationToken token);
}
}

View file

@ -1,9 +1,12 @@
using System.Collections.Generic; using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search namespace Flow.Launcher.Plugin.Explorer.Search
{ {
public interface IPathEnumerable public interface IPathEnumerable
{ {
public IEnumerable<SearchResult> Enumerate(string path, bool recursive); public ValueTask<IEnumerable<SearchResult>> EnumerateAsync(string path, string search, bool recursive, CancellationToken token);
} }
} }

View file

@ -61,11 +61,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search
if (IsFileContentSearch(query.ActionKeyword)) if (IsFileContentSearch(query.ActionKeyword))
{ {
searchResults = await Settings.IndexProvider.ContentSearchAsync(query, token); searchResults = await Settings.ContentIndexProvider.ContentSearchAsync(query.Search, token);
} }
else else
{ {
searchResults = await Settings.IndexProvider.SearchAsync(query, token); searchResults = await Settings.IndexProvider.SearchAsync(query.Search, token);
} }
if (ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) || if (ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) ||
@ -125,7 +125,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath); locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath);
// Check that actual location exists, otherwise directory search will throw directory not found exception // Check that actual location exists, otherwise directory search will throw directory not found exception
if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).LocationExists())
return results.ToList(); return results.ToList();
var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath); var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath);
@ -134,16 +134,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
// var directoryResult = await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync, IEnumerable<SearchResult> directoryResult;
// DirectoryInfoClassSearch,
// useIndexSearch, if (query.Search.Contains('>'))
// query, {
// locationPath, directoryResult =
// token).ConfigureAwait(false); await Settings.PathEnumerator.EnumerateAsync(locationPath, "", false, token)
.ConfigureAwait(false);
}
else
{
directoryResult = DirectoryInfoSearch.TopLevelDirectorySearch(query, query.Search, token);
}
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
// results.UnionWith(directoryResult); results.UnionWith(directoryResult.Select(searchResult => ResultManager.CreateResult(query, searchResult)));
return results.ToList(); return results.ToList();
} }
@ -153,10 +161,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return actionKeyword == Settings.FileContentSearchActionKeyword; return actionKeyword == Settings.FileContentSearchActionKeyword;
} }
private List<Result> DirectoryInfoClassSearch(Query query, string querySearch, CancellationToken token)
{
return DirectoryInfoSearch.TopLevelDirectorySearch(query, querySearch, token);
}
public async Task<List<Result>> TopLevelDirectorySearchBehaviourAsync( public async Task<List<Result>> TopLevelDirectorySearchBehaviourAsync(
Func<Query, string, CancellationToken, Task<List<Result>>> windowsIndexSearch, Func<Query, string, CancellationToken, Task<List<Result>>> windowsIndexSearch,

View file

@ -1,13 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
public interface IIndexProvider
{
public ValueTask<IEnumerable<SearchResult>> SearchAsync(Query query, CancellationToken token);
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(Query query, CancellationToken token);
}
}

View file

@ -19,7 +19,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
// Reserved keywords in oleDB // Reserved keywords in oleDB
private const string ReservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_;\[\]]+$"; private const string ReservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_;\[\]]+$";
private static async Task<List<SearchResult>> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token) private static async Task<List<SearchResult>> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, CancellationToken token)
{ {
var results = new List<SearchResult>(); var results = new List<SearchResult>();
@ -82,7 +82,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
Func<CSearchQueryHelper> createQueryHelper, Func<CSearchQueryHelper> createQueryHelper,
Func<string, string> constructQuery, Func<string, string> constructQuery,
List<AccessLink> exclusionList, List<AccessLink> exclusionList,
Query query,
CancellationToken token) CancellationToken token)
{ {
var regexMatch = Regex.Match(searchString, ReservedStringPattern); var regexMatch = Regex.Match(searchString, ReservedStringPattern);
@ -93,40 +92,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
var constructedQuery = constructQuery(searchString); var constructedQuery = constructQuery(searchString);
return return
await ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, query, token); await ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, token);
} }
private static List<Result> RemoveResultsInExclusionList(List<Result> results, List<AccessLink> exclusionList, CancellationToken token) private static void RemoveResultsInExclusionList(List<SearchResult> results, IReadOnlyList<AccessLink> exclusionList, CancellationToken token)
{ {
var indexExclusionListCount = exclusionList.Count; var indexExclusionListCount = exclusionList.Count;
if (indexExclusionListCount == 0) if (indexExclusionListCount == 0)
return results; return;
results.RemoveAll(searchResult =>
var filteredResults = new List<Result>(); exclusionList.Any(exclude => searchResult.FullPath.StartsWith(exclude.Path, StringComparison.OrdinalIgnoreCase))
);
for (var index = 0; index < results.Count; index++)
{
token.ThrowIfCancellationRequested();
var excludeResult = false;
for (var i = 0; i < indexExclusionListCount; i++)
{
token.ThrowIfCancellationRequested();
if (results[index].SubTitle.StartsWith(exclusionList[i].Path, StringComparison.OrdinalIgnoreCase))
{
excludeResult = true;
break;
}
}
if (!excludeResult)
filteredResults.Add(results[index]);
}
return filteredResults;
} }
internal static bool PathIsIndexed(string path) internal static bool PathIsIndexed(string path)

View file

@ -4,18 +4,18 @@ using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{ {
public class WindowsIndexManager : IIndexProvider public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
{ {
private Settings Settings { get; } private Settings Settings { get; }
private QueryConstructor QueryConstructor { get; } private QueryConstructor QueryConstructor { get; }
public WindowsIndexManager(Settings settings) public WindowsIndexSearchManager(Settings settings)
{ {
Settings = settings; Settings = settings;
QueryConstructor = new QueryConstructor(Settings); QueryConstructor = new QueryConstructor(Settings);
} }
private async Task<List<SearchResult>> WindowsIndexFileContentSearchAsync(Query query, string querySearchString, private async Task<List<SearchResult>> WindowsIndexFileContentSearchAsync(string querySearchString,
CancellationToken token) CancellationToken token)
{ {
if (string.IsNullOrEmpty(querySearchString)) if (string.IsNullOrEmpty(querySearchString))
@ -26,13 +26,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
QueryConstructor.CreateQueryHelper, QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForFileContentSearch, QueryConstructor.QueryForFileContentSearch,
Settings.IndexSearchExcludedSubdirectoryPaths, Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false); token).ConfigureAwait(false);
} }
private async Task<List<SearchResult>> WindowsIndexFilesAndFoldersSearchAsync(Query query, string querySearchString, private async Task<List<SearchResult>> WindowsIndexFilesAndFoldersSearchAsync(string querySearchString,
CancellationToken token) CancellationToken token)
{ {
return await WindowsIndex.WindowsIndexSearchAsync( return await WindowsIndex.WindowsIndexSearchAsync(
@ -40,12 +39,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
QueryConstructor.CreateQueryHelper, QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForAllFilesAndFolders, QueryConstructor.QueryForAllFilesAndFolders,
Settings.IndexSearchExcludedSubdirectoryPaths, Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false); token).ConfigureAwait(false);
} }
private async Task<List<SearchResult>> WindowsIndexTopLevelFolderSearchAsync(Query query, string path, private async Task<List<SearchResult>> WindowsIndexTopLevelFolderSearchAsync(string path,string search,
CancellationToken token) CancellationToken token)
{ {
var queryConstructor = new QueryConstructor(Settings); var queryConstructor = new QueryConstructor(Settings);
@ -55,16 +53,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
queryConstructor.CreateQueryHelper, queryConstructor.CreateQueryHelper,
queryConstructor.QueryForTopLevelDirectorySearch, queryConstructor.QueryForTopLevelDirectorySearch,
Settings.IndexSearchExcludedSubdirectoryPaths, Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false); token).ConfigureAwait(false);
} }
public ValueTask<IEnumerable<SearchResult>> SearchAsync(Query query, CancellationToken token) public async ValueTask<IEnumerable<SearchResult>> SearchAsync(string search, CancellationToken token)
{ {
return default; return await WindowsIndexFilesAndFoldersSearchAsync(search, token);
} }
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(Query query, CancellationToken token) public async ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(string search, CancellationToken token)
{ {
return default; return await WindowsIndexFileContentSearchAsync(search, token);
}
public async ValueTask<IEnumerable<SearchResult>> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
{
if(recursive)
return await WindowsIndexFilesAndFoldersSearchAsync(search, token).ConfigureAwait(false);
return await WindowsIndexTopLevelFolderSearchAsync(path, search, token);
} }
} }
} }

View file

@ -6,6 +6,7 @@ using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Plugin.Explorer namespace Flow.Launcher.Plugin.Explorer
{ {
@ -44,18 +45,44 @@ namespace Flow.Launcher.Plugin.Explorer
public bool WarnWindowsSearchServiceOff { get; set; } = true; public bool WarnWindowsSearchServiceOff { get; set; } = true;
private List<IIndexProvider> _indexProviders; private IReadOnlyList<IIndexProvider> _indexProviders;
private IReadOnlyList<IContentIndexProvider> _fileContentIndexProviders;
private IReadOnlyList<IPathEnumerable> _pathEnumerables;
public Settings() public Settings()
{ {
var everythingManager = new EverythingSearchManager(this);
var windowsIndexManager = new WindowsIndexSearchManager(this);
_indexProviders = new List<IIndexProvider>() _indexProviders = new List<IIndexProvider>()
{ {
new EverythingSearchManager(this), everythingManager,
new WindowsIndexManager(this) windowsIndexManager
};
_pathEnumerables = new List<IPathEnumerable>()
{
everythingManager,
windowsIndexManager
};
_fileContentIndexProviders = new List<IContentIndexProvider>
{
windowsIndexManager, everythingManager
}; };
} }
public IndexSearchEngineOption IndexSearchEngine { get; set; } public IndexSearchEngineOption IndexSearchEngine { get; set; }
[JsonIgnore]
public IIndexProvider IndexProvider => _indexProviders[(int)IndexSearchEngine]; public IIndexProvider IndexProvider => _indexProviders[(int)IndexSearchEngine];
public PathTraversalEngineOption PathEnumerationEngine { get; set; }
[JsonIgnore]
public IPathEnumerable PathEnumerator => _pathEnumerables[(int)PathEnumerationEngine];
public ContentIndexSearchEngineOption ContentSearchEngine { get; set; }
[JsonIgnore]
public IContentIndexProvider ContentIndexProvider => _fileContentIndexProviders[(int)ContentSearchEngine];
public enum PathTraversalEngineOption public enum PathTraversalEngineOption
{ {
Everything, Everything,
@ -68,11 +95,17 @@ namespace Flow.Launcher.Plugin.Explorer
Everything, Everything,
WindowsIndex WindowsIndex
} }
#region Everything Settings
public enum ContentIndexSearchEngineOption
{
Everything,
WindowsIndex
}
public bool LaunchHidden { get; set; } = false; public bool LaunchHidden { get; set; } = false;
#region Everything Settings
public string EverythingInstalledPath { get; set; } public string EverythingInstalledPath { get; set; }
public SortOption[] SortOptions { get; set; } = Enum.GetValues<SortOption>(); public SortOption[] SortOptions { get; set; } = Enum.GetValues<SortOption>();