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>
/// <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="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="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, 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)
throw new ArgumentOutOfRangeException(nameof(offset));
@ -124,6 +129,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
keyword = keyword[1..];
}
if (!string.IsNullOrEmpty(parentPath))
{
keyword += $" {(recursive ? "" : "parent:")}\"{parentPath}\"";
}
EverythingApiDllImport.Everything_SetSearchW(keyword);
EverythingApiDllImport.Everything_SetOffset(offset);
EverythingApiDllImport.Everything_SetMax(maxCount);

View file

@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{
public class EverythingSearchManager : IIndexProvider
public class EverythingSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
{
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>());
}
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
{
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))
{
searchResults = await Settings.IndexProvider.ContentSearchAsync(query, token);
searchResults = await Settings.ContentIndexProvider.ContentSearchAsync(query.Search, token);
}
else
{
searchResults = await Settings.IndexProvider.SearchAsync(query, token);
searchResults = await Settings.IndexProvider.SearchAsync(query.Search, token);
}
if (ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) ||
@ -120,12 +120,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search
var isEnvironmentVariablePath = querySearch[1..].Contains("%\\");
var locationPath = querySearch;
if (isEnvironmentVariablePath)
locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath);
// 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();
var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath);
@ -134,16 +134,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search
token.ThrowIfCancellationRequested();
// var directoryResult = await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync,
// DirectoryInfoClassSearch,
// useIndexSearch,
// query,
// locationPath,
// token).ConfigureAwait(false);
IEnumerable<SearchResult> directoryResult;
if (query.Search.Contains('>'))
{
directoryResult =
await Settings.PathEnumerator.EnumerateAsync(locationPath, "", false, token)
.ConfigureAwait(false);
}
else
{
directoryResult = DirectoryInfoSearch.TopLevelDirectorySearch(query, query.Search, token);
}
token.ThrowIfCancellationRequested();
// results.UnionWith(directoryResult);
results.UnionWith(directoryResult.Select(searchResult => ResultManager.CreateResult(query, searchResult)));
return results.ToList();
}
@ -153,10 +161,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
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(
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
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>();
@ -82,7 +82,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
Func<CSearchQueryHelper> createQueryHelper,
Func<string, string> constructQuery,
List<AccessLink> exclusionList,
Query query,
CancellationToken token)
{
var regexMatch = Regex.Match(searchString, ReservedStringPattern);
@ -93,40 +92,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
var constructedQuery = constructQuery(searchString);
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;
if (indexExclusionListCount == 0)
return results;
var filteredResults = new List<Result>();
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;
return;
results.RemoveAll(searchResult =>
exclusionList.Any(exclude => searchResult.FullPath.StartsWith(exclude.Path, StringComparison.OrdinalIgnoreCase))
);
}
internal static bool PathIsIndexed(string path)

View file

@ -4,18 +4,18 @@ using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
public class WindowsIndexManager : IIndexProvider
public class WindowsIndexSearchManager : IIndexProvider, IContentIndexProvider, IPathEnumerable
{
private Settings Settings { get; }
private QueryConstructor QueryConstructor { get; }
public WindowsIndexManager(Settings settings)
public WindowsIndexSearchManager(Settings settings)
{
Settings = 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)
{
if (string.IsNullOrEmpty(querySearchString))
@ -26,13 +26,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForFileContentSearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false);
}
private async Task<List<SearchResult>> WindowsIndexFilesAndFoldersSearchAsync(Query query, string querySearchString,
private async Task<List<SearchResult>> WindowsIndexFilesAndFoldersSearchAsync(string querySearchString,
CancellationToken token)
{
return await WindowsIndex.WindowsIndexSearchAsync(
@ -40,12 +39,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForAllFilesAndFolders,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
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)
{
var queryConstructor = new QueryConstructor(Settings);
@ -55,16 +53,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForTopLevelDirectorySearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
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.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Plugin.Explorer
{
@ -44,18 +45,44 @@ namespace Flow.Launcher.Plugin.Explorer
public bool WarnWindowsSearchServiceOff { get; set; } = true;
private List<IIndexProvider> _indexProviders;
private IReadOnlyList<IIndexProvider> _indexProviders;
private IReadOnlyList<IContentIndexProvider> _fileContentIndexProviders;
private IReadOnlyList<IPathEnumerable> _pathEnumerables;
public Settings()
{
var everythingManager = new EverythingSearchManager(this);
var windowsIndexManager = new WindowsIndexSearchManager(this);
_indexProviders = new List<IIndexProvider>()
{
new EverythingSearchManager(this),
new WindowsIndexManager(this)
everythingManager,
windowsIndexManager
};
_pathEnumerables = new List<IPathEnumerable>()
{
everythingManager,
windowsIndexManager
};
_fileContentIndexProviders = new List<IContentIndexProvider>
{
windowsIndexManager, everythingManager
};
}
public IndexSearchEngineOption IndexSearchEngine { get; set; }
[JsonIgnore]
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
{
Everything,
@ -68,11 +95,17 @@ namespace Flow.Launcher.Plugin.Explorer
Everything,
WindowsIndex
}
#region Everything Settings
public enum ContentIndexSearchEngineOption
{
Everything,
WindowsIndex
}
public bool LaunchHidden { get; set; } = false;
#region Everything Settings
public string EverythingInstalledPath { get; set; }
public SortOption[] SortOptions { get; set; } = Enum.GetValues<SortOption>();