mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
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:
parent
b0f9b488b3
commit
6fdff2971c
19 changed files with 642 additions and 126 deletions
Binary file not shown.
Binary file not shown.
|
|
@ -35,6 +35,9 @@
|
|||
<SubType>Designer</SubType>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="EverythingSDK\*.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs
Normal file
14
Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchResult.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue