Merge Explorer & Everything Plugins (Step 1)

1. Shared Interface for Windows Index and Everything Index
2. Settings Merge (Part 1)
3. Include Everything dll
This commit is contained in:
Hongtao Zhang 2022-03-25 16:19:00 -05:00
parent b0f9b488b3
commit 6fdff2971c
No known key found for this signature in database
GPG key ID: 75F655B91C7AC9BB
19 changed files with 642 additions and 126 deletions

View file

@ -35,6 +35,9 @@
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="EverythingSDK\*.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View file

@ -1,9 +1,12 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.Everything;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using Flow.Launcher.Plugin.Explorer.ViewModels;
using Flow.Launcher.Plugin.Explorer.Views;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -47,7 +50,8 @@ namespace Flow.Launcher.Plugin.Explorer
contextMenu = new ContextMenu(Context, Settings, viewModel);
searchManager = new SearchManager(Settings, Context);
ResultManager.Init(Context, Settings);
EverythingApiDllImport.Load(Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "EverythingSDK",
Environment.Is64BitProcess ? "Everything64.dll" : "Everything86.dll"));
return Task.CompletedTask;
}

View file

@ -0,0 +1,193 @@
using Flow.Launcher.Plugin.Everything.Everything;
using Flow.Launcher.Plugin.Everything.Everything.Exceptions;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{
public static class EverythingApi
{
private const int BufferSize = 4096;
private static readonly object syncObject = new object();
// cached buffer to remove redundant allocations.
private static readonly StringBuilder buffer = new StringBuilder(BufferSize);
public enum StateCode
{
OK,
MemoryError,
IPCError,
RegisterClassExError,
CreateWindowError,
CreateThreadError,
InvalidIndexError,
InvalidCallError
}
/// <summary>
/// Gets or sets a value indicating whether [match path].
/// </summary>
/// <value><c>true</c> if [match path]; otherwise, <c>false</c>.</value>
public static bool MatchPath
{
get => EverythingApiDllImport.Everything_GetMatchPath();
set => EverythingApiDllImport.Everything_SetMatchPath(value);
}
/// <summary>
/// Gets or sets a value indicating whether [match case].
/// </summary>
/// <value><c>true</c> if [match case]; otherwise, <c>false</c>.</value>
public static bool MatchCase
{
get => EverythingApiDllImport.Everything_GetMatchCase();
set => EverythingApiDllImport.Everything_SetMatchCase(value);
}
/// <summary>
/// Gets or sets a value indicating whether [match whole word].
/// </summary>
/// <value><c>true</c> if [match whole word]; otherwise, <c>false</c>.</value>
public static bool MatchWholeWord
{
get => EverythingApiDllImport.Everything_GetMatchWholeWord();
set => EverythingApiDllImport.Everything_SetMatchWholeWord(value);
}
/// <summary>
/// Gets or sets a value indicating whether [enable regex].
/// </summary>
/// <value><c>true</c> if [enable regex]; otherwise, <c>false</c>.</value>
public static bool EnableRegex
{
get => EverythingApiDllImport.Everything_GetRegex();
set => EverythingApiDllImport.Everything_SetRegex(value);
}
/// <summary>
/// Resets this instance.
/// </summary>
private static void Reset()
{
lock (syncObject)
{
EverythingApiDllImport.Everything_Reset();
}
}
/// <summary>
/// Checks whether the sort option is Fast Sort.
/// </summary>
public static bool IsFastSortOption(SortOption sortOption)
{
var fastSortOptionEnabled = EverythingApiDllImport.Everything_IsFastSort(sortOption);
// If the Everything service is not running, then this call will incorrectly report
// the state as false. This checks for errors thrown by the api and up to the caller to handle.
CheckAndThrowExceptionOnError();
return fastSortOptionEnabled;
}
/// <summary>
/// Searches the specified key word and reset the everything API afterwards
/// </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="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)
{
if (string.IsNullOrEmpty(keyword))
throw new ArgumentNullException(nameof(keyword));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset));
if (maxCount < 0)
throw new ArgumentOutOfRangeException(nameof(maxCount));
lock (syncObject)
{
if (keyword.StartsWith("@"))
{
EverythingApiDllImport.Everything_SetRegex(true);
keyword = keyword[1..];
}
EverythingApiDllImport.Everything_SetSearchW(keyword);
EverythingApiDllImport.Everything_SetOffset(offset);
EverythingApiDllImport.Everything_SetMax(maxCount);
EverythingApiDllImport.Everything_SetSort(sortOption);
if (token.IsCancellationRequested)
{
return null;
}
if (!EverythingApiDllImport.Everything_QueryW(true))
{
CheckAndThrowExceptionOnError();
return null;
}
var results = new List<SearchResult>();
for (var idx = 0; idx < EverythingApiDllImport.Everything_GetNumResults(); ++idx)
{
if (token.IsCancellationRequested)
{
return null;
}
EverythingApiDllImport.Everything_GetResultFullPathNameW(idx, buffer, BufferSize);
var result = new SearchResult
{
FullPath = buffer.ToString(),
Type = EverythingApiDllImport.Everything_IsFolderResult(idx) ? ResultType.Folder :
EverythingApiDllImport.Everything_IsFileResult(idx) ? ResultType.File :
ResultType.Volume
};
results.Add(result);
}
Reset();
return results;
}
}
private static void CheckAndThrowExceptionOnError()
{
switch (EverythingApiDllImport.Everything_GetLastError())
{
case StateCode.CreateThreadError:
throw new CreateThreadException();
case StateCode.CreateWindowError:
throw new CreateWindowException();
case StateCode.InvalidCallError:
throw new InvalidCallException();
case StateCode.InvalidIndexError:
throw new InvalidIndexException();
case StateCode.IPCError:
throw new IPCErrorException();
case StateCode.MemoryError:
throw new MemoryErrorException();
case StateCode.RegisterClassExError:
throw new RegisterClassExException();
}
}
}
}

View file

@ -0,0 +1,153 @@
using Flow.Launcher.Plugin.Everything.Everything;
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{
public static class EverythingApiDllImport
{
public static void Load(string path)
{
LoadLibrary(path);
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern int LoadLibrary(string name);
private const string DLL = "Everything.dll";
[DllImport(DLL, CharSet = CharSet.Unicode)]
internal static extern int Everything_SetSearchW(string lpSearchString);
[DllImport(DLL)]
internal static extern void Everything_SetMatchPath(bool bEnable);
[DllImport(DLL)]
internal static extern void Everything_SetMatchCase(bool bEnable);
[DllImport(DLL)]
internal static extern void Everything_SetMatchWholeWord(bool bEnable);
[DllImport(DLL)]
internal static extern void Everything_SetRegex(bool bEnable);
[DllImport(DLL)]
internal static extern void Everything_SetMax(int dwMax);
[DllImport(DLL)]
internal static extern void Everything_SetOffset(int dwOffset);
[DllImport(DLL)]
internal static extern bool Everything_GetMatchPath();
[DllImport(DLL)]
internal static extern bool Everything_GetMatchCase();
[DllImport(DLL)]
internal static extern bool Everything_GetMatchWholeWord();
[DllImport(DLL)]
internal static extern bool Everything_GetRegex();
[DllImport(DLL)]
internal static extern uint Everything_GetMax();
[DllImport(DLL)]
internal static extern uint Everything_GetOffset();
[DllImport(DLL, CharSet = CharSet.Unicode)]
internal static extern string Everything_GetSearchW();
[DllImport(DLL)]
internal static extern EverythingApi.StateCode Everything_GetLastError();
[DllImport(DLL, CharSet = CharSet.Unicode)]
internal static extern bool Everything_QueryW(bool bWait);
[DllImport(DLL)]
internal static extern void Everything_SortResultsByPath();
[DllImport(DLL)]
internal static extern int Everything_GetNumFileResults();
[DllImport(DLL)]
internal static extern int Everything_GetNumFolderResults();
[DllImport(DLL)]
internal static extern int Everything_GetNumResults();
[DllImport(DLL)]
internal static extern int Everything_GetTotFileResults();
[DllImport(DLL)]
internal static extern int Everything_GetTotFolderResults();
[DllImport(DLL)]
internal static extern int Everything_GetTotResults();
[DllImport(DLL)]
internal static extern bool Everything_IsVolumeResult(int nIndex);
[DllImport(DLL)]
internal static extern bool Everything_IsFolderResult(int nIndex);
[DllImport(DLL)]
internal static extern bool Everything_IsFileResult(int nIndex);
[DllImport(DLL, CharSet = CharSet.Unicode)]
internal static extern void Everything_GetResultFullPathNameW(int nIndex, StringBuilder lpString, int nMaxCount);
[DllImport(DLL)]
internal static extern void Everything_Reset();
// Everything 1.4
[DllImport(DLL)]
public static extern void Everything_SetSort(SortOption dwSortType);
[DllImport(DLL)]
public static extern bool Everything_IsFastSort(SortOption dwSortType);
[DllImport(DLL)]
public static extern SortOption Everything_GetSort();
[DllImport(DLL)]
public static extern uint Everything_GetResultListSort();
[DllImport(DLL)]
public static extern void Everything_SetRequestFlags(uint dwRequestFlags);
[DllImport(DLL)]
public static extern uint Everything_GetRequestFlags();
[DllImport(DLL)]
public static extern uint Everything_GetResultListRequestFlags();
[DllImport("Everything64.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr Everything_GetResultExtension(uint nIndex);
[DllImport(DLL)]
public static extern bool Everything_GetResultSize(uint nIndex, out long lpFileSize);
[DllImport(DLL)]
public static extern bool Everything_GetResultDateCreated(uint nIndex, out long lpFileTime);
[DllImport(DLL)]
public static extern bool Everything_GetResultDateModified(uint nIndex, out long lpFileTime);
[DllImport(DLL)]
public static extern bool Everything_GetResultDateAccessed(uint nIndex, out long lpFileTime);
[DllImport(DLL)]
public static extern uint Everything_GetResultAttributes(uint nIndex);
[DllImport(DLL, CharSet = CharSet.Unicode)]
public static extern IntPtr Everything_GetResultFileListFileName(uint nIndex);
[DllImport(DLL)]
public static extern uint Everything_GetResultRunCount(uint nIndex);
[DllImport(DLL)]
public static extern bool Everything_GetResultDateRun(uint nIndex, out long lpFileTime);
[DllImport(DLL)]
public static extern bool Everything_GetResultDateRecentlyChanged(uint nIndex, out long lpFileTime);
[DllImport(DLL, CharSet = CharSet.Unicode)]
public static extern IntPtr Everything_GetResultHighlightedFileName(uint nIndex);
[DllImport(DLL, CharSet = CharSet.Unicode)]
public static extern IntPtr Everything_GetResultHighlightedPath(uint nIndex);
[DllImport(DLL, CharSet = CharSet.Unicode)]
public static extern IntPtr Everything_GetResultHighlightedFullPathAndFileName(uint nIndex);
[DllImport(DLL)]
public static extern uint Everything_GetRunCountFromFileName(string lpFileName);
[DllImport(DLL)]
public static extern bool Everything_SetRunCountFromFileName(string lpFileName, uint dwRunCount);
[DllImport(DLL)]
public static extern uint Everything_IncRunCountFromFileName(string lpFileName);
}
}

View file

@ -0,0 +1,27 @@
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{
public class EverythingSearchManager : IIndexProvider
{
private Settings Settings { get; }
public EverythingSearchManager(Settings settings)
{
Settings = settings;
}
public ValueTask<IEnumerable<SearchResult>> SearchAsync(Query query, CancellationToken token)
{
return ValueTask.FromResult(EverythingApi.SearchAsync(query.Search, token, Settings.SortOption));
}
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(Query query, CancellationToken token)
{
return new ValueTask<IEnumerable<SearchResult>>(new List<SearchResult>());
}
}
}

View file

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Everything.Everything
{
public enum SortOption : uint
{
NAME_ASCENDING = 1u,
NAME_DESCENDING = 2u,
PATH_ASCENDING = 3u,
PATH_DESCENDING = 4u,
SIZE_ASCENDING = 5u,
SIZE_DESCENDING = 6u,
EXTENSION_ASCENDING = 7u,
EXTENSION_DESCENDING = 8u,
TYPE_NAME_ASCENDING = 9u,
TYPE_NAME_DESCENDING = 10u,
DATE_CREATED_ASCENDING = 11u,
DATE_CREATED_DESCENDING = 12u,
DATE_MODIFIED_ASCENDING = 13u,
DATE_MODIFIED_DESCENDING = 14u,
ATTRIBUTES_ASCENDING = 15u,
ATTRIBUTES_DESCENDING = 16u,
FILE_LIST_FILENAME_ASCENDING = 17u,
FILE_LIST_FILENAME_DESCENDING = 18u,
RUN_COUNT_ASCENDING = 19u,
RUN_COUNT_DESCENDING = 20u,
DATE_RECENTLY_CHANGED_ASCENDING = 21u,
DATE_RECENTLY_CHANGED_DESCENDING = 22u,
DATE_ACCESSED_ASCENDING = 23u,
DATE_ACCESSED_DESCENDING = 24u,
DATE_RUN_ASCENDING = 25u,
DATE_RUN_DESCENDING = 26u
}
}

View file

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Flow.Launcher.Plugin.Explorer.Search
{
public interface IPathEnumerable
{
public IEnumerable<SearchResult> Enumerate(string path, bool recursive);
}
}

View file

@ -35,6 +35,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return $"{keyword}{formatted_path}";
}
public static Result CreateResult(Query query, SearchResult result)
{
return result.Type switch
{
ResultType.Folder or ResultType.Volume => CreateFolderResult(Path.GetFileName(result.FullPath),
result.FullPath, result.FullPath, query, 0, result.ShowIndexState, result.WindowsIndexed),
ResultType.File => CreateFileResult(
result.FullPath, query, 0, result.ShowIndexState, result.WindowsIndexed),
_ => throw new ArgumentOutOfRangeException()
};
}
internal static Result CreateFolderResult(string title, string subtitle, string path, Query query, int score = 0, bool showIndexState = false, bool windowsIndexed = false)
{
return new Result
@ -61,7 +73,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
}
Context.API.ChangeQuery(GetPathWithActionKeyword(path, ResultType.Folder));
return false;
},
Score = score,
@ -192,16 +204,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
}
}
internal class SearchResult
{
public string FullPath { get; set; }
public ResultType Type { get; set; }
public bool WindowsIndexed { get; set; }
public bool ShowIndexState { get; set; }
}
public enum ResultType
{
Volume,

View file

@ -57,8 +57,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search
results.UnionWith(quickaccessLinks);
}
IEnumerable<SearchResult> searchResults;
if (IsFileContentSearch(query.ActionKeyword))
return await WindowsIndexFileContentSearchAsync(query, querySearch, token).ConfigureAwait(false);
{
searchResults = await Settings.IndexProvider.ContentSearchAsync(query, token);
}
else
{
searchResults = await Settings.IndexProvider.SearchAsync(query, token);
}
if (ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) ||
ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword))
@ -71,8 +79,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
querySearch.Length > 0 &&
!querySearch.IsLocationPathString())
{
results.UnionWith(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token)
.ConfigureAwait(false));
if (searchResults != null)
results.UnionWith(searchResults.Select(search => ResultManager.CreateResult(query, search)));
}
return results.ToList();
@ -93,7 +101,7 @@ 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
};
}
@ -126,37 +134,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search
token.ThrowIfCancellationRequested();
var directoryResult = await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync,
DirectoryInfoClassSearch,
useIndexSearch,
query,
locationPath,
token).ConfigureAwait(false);
// var directoryResult = await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync,
// DirectoryInfoClassSearch,
// useIndexSearch,
// query,
// locationPath,
// token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
results.UnionWith(directoryResult);
// results.UnionWith(directoryResult);
return results.ToList();
}
private async Task<List<Result>> WindowsIndexFileContentSearchAsync(Query query, string querySearchString,
CancellationToken token)
{
var queryConstructor = new QueryConstructor(Settings);
if (string.IsNullOrEmpty(querySearchString))
return new List<Result>();
return await IndexSearch.WindowsIndexSearchAsync(
querySearchString,
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForFileContentSearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false);
}
public bool IsFileContentSearch(string actionKeyword)
{
return actionKeyword == Settings.FileContentSearchActionKeyword;
@ -181,34 +172,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return await windowsIndexSearch(query, querySearchString, token);
}
private async Task<List<Result>> WindowsIndexFilesAndFoldersSearchAsync(Query query, string querySearchString,
CancellationToken token)
{
var queryConstructor = new QueryConstructor(Settings);
return await IndexSearch.WindowsIndexSearchAsync(
querySearchString,
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForAllFilesAndFolders,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false);
}
private async Task<List<Result>> WindowsIndexTopLevelFolderSearchAsync(Query query, string path,
CancellationToken token)
{
var queryConstructor = new QueryConstructor(Settings);
return await IndexSearch.WindowsIndexSearchAsync(
path,
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForTopLevelDirectorySearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false);
}
private bool UseWindowsIndexForDirectorySearch(string locationPath)
{
var pathToDirectory = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath);
@ -221,7 +184,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
.StartsWith(x.Path, StringComparison.OrdinalIgnoreCase)))
return false;
return IndexSearch.PathIsIndexed(pathToDirectory);
return WindowsIndex.WindowsIndex.PathIsIndexed(pathToDirectory);
}
}
}

View file

@ -0,0 +1,14 @@
using System;
namespace Flow.Launcher.Plugin.Explorer.Search
{
public struct SearchResult
{
public string FullPath { get; init; }
public ResultType Type { get; init; }
public bool WindowsIndexed { get; init; }
public bool ShowIndexState { get; init; }
}
}

View file

@ -0,0 +1,13 @@
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

@ -13,16 +13,15 @@ using System.Windows;
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
internal static class IndexSearch
internal static class WindowsIndex
{
// Reserved keywords in oleDB
private const string reservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_;\[\]]+$";
private const string ReservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_;\[\]]+$";
internal static async Task<List<Result>> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token)
private static async Task<List<SearchResult>> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token)
{
var results = new List<Result>();
var fileResults = new List<Result>();
var results = new List<SearchResult>();
try
{
@ -35,7 +34,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
await using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader;
token.ThrowIfCancellationRequested();
if (dataReaderResults.HasRows)
if (dataReaderResults is { HasRows: true })
{
while (await dataReaderResults.ReadAsync(token))
{
@ -49,18 +48,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
var path = new Uri(encodedFragmentPath).LocalPath;
if (dataReaderResults.GetString(2) == "Directory")
results.Add(new SearchResult()
{
results.Add(ResultManager.CreateFolderResult(
dataReaderResults.GetString(0),
path,
path,
query, 0, true, true));
}
else
{
fileResults.Add(ResultManager.CreateFileResult(path, query, 0, true, true));
}
FullPath = path,
Type = dataReaderResults.GetString(2) == "Directory" ? ResultType.Folder : ResultType.File,
WindowsIndexed = true
});
}
}
}
@ -80,13 +73,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
LogException("General error from performing index search", e);
}
results.AddRange(fileResults);
// Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
return results;
// Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
return results;
}
internal async static Task<List<Result>> WindowsIndexSearchAsync(
internal static async ValueTask<List<SearchResult>> WindowsIndexSearchAsync(
string searchString,
Func<CSearchQueryHelper> createQueryHelper,
Func<string, string> constructQuery,
@ -94,28 +85,15 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
Query query,
CancellationToken token)
{
var regexMatch = Regex.Match(searchString, reservedStringPattern);
var regexMatch = Regex.Match(searchString, ReservedStringPattern);
if (regexMatch.Success)
return new List<Result>();
try
{
var constructedQuery = constructQuery(searchString);
return new();
return RemoveResultsInExclusionList(
await ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, query, token).ConfigureAwait(false),
exclusionList,
token);
}
catch (COMException)
{
// Occurs because the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin
if (!SearchManager.Settings.WarnWindowsSearchServiceOff)
return new List<Result>();
var constructedQuery = constructQuery(searchString);
return ResultForWindexSearchOff(query.RawQuery);
}
return
await ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, query, token);
}
private static List<Result> RemoveResultsInExclusionList(List<Result> results, List<AccessLink> exclusionList, CancellationToken token)
@ -159,7 +137,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
var indexManager = csm.GetCatalog("SystemIndex").GetCrawlScopeManager();
return indexManager.IncludedInCrawlScope(path) > 0;
}
catch(COMException)
catch (COMException)
{
// Occurs because the Windows Indexing (WSearch) is turned off in services and unable to be used by Explorer plugin
return false;
@ -180,18 +158,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
SearchManager.Settings.WarnWindowsSearchServiceOff = false;
var pluginsManagerPlugin= api.GetAllPlugins().FirstOrDefault(x => x.Metadata.ID == "9f8f9b14-2518-4907-b211-35ab6290dee7");
var pluginsManagerPlugin = api.GetAllPlugins().FirstOrDefault(x => x.Metadata.ID == "9f8f9b14-2518-4907-b211-35ab6290dee7");
var actionKeywordCount = pluginsManagerPlugin.Metadata.ActionKeywords.Count;
if (actionKeywordCount > 1)
LogException("PluginsManager's action keyword has increased to more than 1, this does not allow for determining the " +
"right action keyword. Explorer's code for managing Windows Search service not running exception needs to be updated",
"right action keyword. Explorer's code for managing Windows Search service not running exception needs to be updated",
new InvalidOperationException());
if (MessageBox.Show(string.Format(api.GetTranslation("plugin_explorer_alternative"), Environment.NewLine),
api.GetTranslation("plugin_explorer_alternative_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes
api.GetTranslation("plugin_explorer_alternative_title"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes
&& actionKeywordCount == 1)
{
api.ChangeQuery(string.Format("{0} install everything", pluginsManagerPlugin.Metadata.ActionKeywords[0]));
@ -221,7 +199,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
throw e;
#else
Log.Exception($"|Flow.Launcher.Plugin.Explorer.IndexSearch|{message}", e);
#endif
#endif
}
}
}
}

View file

@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
public class WindowsIndexManager : IIndexProvider
{
private Settings Settings { get; }
private QueryConstructor QueryConstructor { get; }
public WindowsIndexManager(Settings settings)
{
Settings = settings;
QueryConstructor = new QueryConstructor(Settings);
}
private async Task<List<SearchResult>> WindowsIndexFileContentSearchAsync(Query query, string querySearchString,
CancellationToken token)
{
if (string.IsNullOrEmpty(querySearchString))
return new List<SearchResult>();
return await WindowsIndex.WindowsIndexSearchAsync(
querySearchString,
QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForFileContentSearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false);
}
private async Task<List<SearchResult>> WindowsIndexFilesAndFoldersSearchAsync(Query query, string querySearchString,
CancellationToken token)
{
return await WindowsIndex.WindowsIndexSearchAsync(
querySearchString,
QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForAllFilesAndFolders,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false);
}
private async Task<List<SearchResult>> WindowsIndexTopLevelFolderSearchAsync(Query query, string path,
CancellationToken token)
{
var queryConstructor = new QueryConstructor(Settings);
return await WindowsIndex.WindowsIndexSearchAsync(
path,
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForTopLevelDirectorySearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
query,
token).ConfigureAwait(false);
}
public ValueTask<IEnumerable<SearchResult>> SearchAsync(Query query, CancellationToken token)
{
return default;
}
public ValueTask<IEnumerable<SearchResult>> ContentSearchAsync(Query query, CancellationToken token)
{
return default;
}
}
}

View file

@ -1,7 +1,11 @@
using Flow.Launcher.Plugin.Everything.Everything;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.Everything;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Flow.Launcher.Plugin.Explorer
{
@ -40,6 +44,43 @@ namespace Flow.Launcher.Plugin.Explorer
public bool WarnWindowsSearchServiceOff { get; set; } = true;
private List<IIndexProvider> _indexProviders;
public Settings()
{
_indexProviders = new List<IIndexProvider>()
{
new EverythingSearchManager(this),
new WindowsIndexManager(this)
};
}
public IndexSearchEngineOption IndexSearchEngine { get; set; }
public IIndexProvider IndexProvider => _indexProviders[(int)IndexSearchEngine];
public enum PathTraversalEngineOption
{
Everything,
WindowsIndex,
Direct
}
public enum IndexSearchEngineOption
{
Everything,
WindowsIndex
}
#region Everything Settings
public bool LaunchHidden { get; set; } = false;
public string EverythingInstalledPath { get; set; }
public SortOption[] SortOptions { get; set; } = Enum.GetValues<SortOption>();
public SortOption SortOption { get; set; } = SortOption.NAME_ASCENDING;
#endregion
internal enum ActionKeyword
{
SearchActionKeyword,

View file

@ -9,7 +9,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
{
public class SettingsViewModel
{
internal Settings Settings { get; set; }
public Settings Settings { get; set; }
internal PluginInitContext Context { get; set; }
@ -29,7 +29,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
internal void RemoveAccessLinkFromExcludedIndexPaths(AccessLink selectedRow) => Settings.IndexSearchExcludedSubdirectoryPaths.Remove(selectedRow);
internal void OpenWindowsIndexingOptions()
internal static void OpenWindowsIndexingOptions()
{
var psi = new ProcessStartInfo
{

View file

@ -6,14 +6,13 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Explorer.ViewModels"
xmlns:views="clr-namespace:Flow.Launcher.Plugin.Explorer.Views"
xmlns:qa="clr-namespace:Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
mc:Ignorable="d"
d:DataContext="{d:DesignInstance viewModels:SettingsViewModel}">
<UserControl.Resources>
<DataTemplate x:Key="ListViewTemplateAccessLinks">
<TextBlock Margin="0,5,0,5" Text="{Binding Path, Mode=OneTime}" />
</DataTemplate>
<DataTemplate x:Key="ListViewTemplateExcludedPaths">
<DataTemplate x:Key="ListViewTemplateAccessLinks" DataType="qa:AccessLink">
<TextBlock Margin="0,5,0,5" Text="{Binding Path, Mode=OneTime}" />
</DataTemplate>
<DataTemplate x:Key="ListViewActionKeywords" DataType="views:ActionKeywordView">
@ -97,8 +96,14 @@
AllowDrop="True"
DragEnter="lbxAccessLinks_DragEnter"
Drop="lbxAccessLinks_Drop"
ItemTemplate="{StaticResource ListViewTemplateExcludedPaths}" />
ItemTemplate="{StaticResource ListViewActionKeywords}" />
</Expander>
<ComboBox Grid.Row="4"
Grid.Column="1"
Width="200"
SelectedItem="{Binding Settings.SortOption}"
ItemsSource="{Binding Settings.SortOptions, Mode=OneWay}">
</ComboBox>
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="1">
@ -144,4 +149,4 @@
</StackPanel>
</Grid>
</Grid>
</UserControl>
</UserControl>

View file

@ -25,6 +25,8 @@ namespace Flow.Launcher.Plugin.Explorer.Views
public ExplorerSettings(SettingsViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
this.viewModel = viewModel;
@ -310,7 +312,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
private void btnOpenIndexingOptions_Click(object sender, RoutedEventArgs e)
{
viewModel.OpenWindowsIndexingOptions();
SettingsViewModel.OpenWindowsIndexingOptions();
}
public void SetButtonVisibilityToHidden()