mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Support Path Enumeration (Part 1)
This commit is contained in:
parent
b671f562ef
commit
fa0cd35e18
10 changed files with 129 additions and 85 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>();
|
||||
|
|
|
|||
Loading…
Reference in a new issue