diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index 41072993c..55f2f3a7e 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -68,6 +68,7 @@
+
diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs
index 9d7fccad9..7e379e3f8 100644
--- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs
+++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs
@@ -7,6 +7,7 @@ using Flow.Launcher.Plugin.SharedCommands;
using NUnit.Framework;
using System;
using System.Collections.Generic;
+using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
@@ -44,6 +45,7 @@ namespace Flow.Launcher.Test.Plugins
private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false;
+ [SupportedOSPlatform("windows7.0")]
[TestCase("C:\\SomeFolder\\", "directory='file:C:\\SomeFolder\\'")]
public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString)
{
@@ -60,6 +62,7 @@ namespace Flow.Launcher.Test.Plugins
$"Actual: {result}{Environment.NewLine}");
}
+ [SupportedOSPlatform("windows7.0")]
[TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")]
[TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")]
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
@@ -76,10 +79,11 @@ namespace Flow.Launcher.Test.Plugins
$"Actual string was: {queryString}{Environment.NewLine}");
}
+ [SupportedOSPlatform("windows7.0")]
[TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
- "FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
- "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" +
- " AND directory='file:C:\\SomeFolder' ORDER BY System.FileName")]
+ "FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
+ "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" +
+ " AND directory='file:C:\\SomeFolder' ORDER BY System.FileName")]
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
string userSearchString, string expectedString)
{
@@ -94,10 +98,11 @@ namespace Flow.Launcher.Test.Plugins
$"Expected string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {queryString}{Environment.NewLine}");
}
-
+
+ [SupportedOSPlatform("windows7.0")]
[TestCase("C:\\SomeFolder\\SomeApp", "(System.FileName LIKE 'SomeApp%' " +
- "OR CONTAINS(System.FileName,'\"SomeApp*\"',1033))" +
- " AND directory='file:C:\\SomeFolder'")]
+ "OR CONTAINS(System.FileName,'\"SomeApp*\"',1033))" +
+ " AND directory='file:C:\\SomeFolder'")]
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryWhereRestrictionsShouldUseDirectoryString(
string userSearchString, string expectedString)
{
@@ -113,6 +118,7 @@ namespace Flow.Launcher.Test.Plugins
$"Actual string was: {queryString}{Environment.NewLine}");
}
+ [SupportedOSPlatform("windows7.0")]
[TestCase("scope='file:'")]
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString)
{
@@ -125,9 +131,10 @@ namespace Flow.Launcher.Test.Plugins
$"Actual string was: {resultString}{Environment.NewLine}");
}
+ [SupportedOSPlatform("windows7.0")]
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " +
- "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
- "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")]
+ "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
+ "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")]
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
string userSearchString, string expectedString)
{
@@ -149,49 +156,8 @@ namespace Flow.Launcher.Test.Plugins
$"Expected query string: {expectedString}{Environment.NewLine} " +
$"Actual string was: {resultString}{Environment.NewLine}");
}
-
- [TestCase]
- public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearch()
- {
- // Given
- var searchManager = new SearchManager(new Settings(), new PluginInitContext());
-
- // When
- var results = await searchManager.TopLevelDirectorySearchBehaviourAsync(
- MethodWindowsIndexSearchReturnsZeroResultsAsync,
- MethodDirectoryInfoClassSearchReturnsTwoResults,
- false,
- new Query(),
- "string not used",
- default);
-
- // Then
- Assert.IsTrue(results.Count == 2,
- $"Expected to have 2 results from DirectoryInfoClassSearch {Environment.NewLine} " +
- $"Actual number of results is {results.Count} {Environment.NewLine}");
- }
-
- [TestCase]
- public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearch()
- {
- // Given
- var searchManager = new SearchManager(new Settings(), new PluginInitContext());
-
- // When
- var results = await searchManager.TopLevelDirectorySearchBehaviourAsync(
- MethodWindowsIndexSearchReturnsZeroResultsAsync,
- MethodDirectoryInfoClassSearchReturnsTwoResults,
- true,
- new Query(),
- "string not used",
- default);
-
- // Then
- Assert.IsTrue(results.Count == 0,
- $"Expected to have 0 results because location is indexed {Environment.NewLine} " +
- $"Actual number of results is {results.Count} {Environment.NewLine}");
- }
-
+
+ [SupportedOSPlatform("windows7.0")]
[TestCase(@"some words", @"FREETEXT('some words')")]
public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString(
string querySearchString, string expectedString)
@@ -208,6 +174,7 @@ namespace Flow.Launcher.Test.Plugins
$"Actual string was: {resultString}{Environment.NewLine}");
}
+ [SupportedOSPlatform("windows7.0")]
[TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")]
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
@@ -233,7 +200,7 @@ namespace Flow.Launcher.Test.Plugins
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
// When
- var result = searchManager.IsFileContentSearch(query.ActionKeyword);
+ var result = SearchManager.IsFileContentSearch(query.ActionKeyword);
// Then
Assert.IsTrue(result,
@@ -301,6 +268,7 @@ namespace Flow.Launcher.Test.Plugins
$"Actual path string is {returnedPath} {Environment.NewLine}");
}
+ [SupportedOSPlatform("windows7.0")]
[TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")]
[TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' "
+ "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND "
@@ -319,6 +287,7 @@ namespace Flow.Launcher.Test.Plugins
$"Actual string was: {resultString}{Environment.NewLine}");
}
+ [SupportedOSPlatform("windows7.0")]
[TestCase("c:\\somefolder\\>somefile", "*somefile*")]
[TestCase("c:\\somefolder\\somefile", "somefile*")]
[TestCase("c:\\somefolder\\", "*")]
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
index 82eecd8fd..9c9734967 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs
@@ -33,10 +33,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo
{
var indexOfSeparator = search.LastIndexOf(Constants.DirectorySeperator);
- incompleteName = search.Substring(indexOfSeparator + 1).ToLower();
+ incompleteName = search[(indexOfSeparator + 1)..].ToLower();
if (incompleteName.StartsWith(Constants.AllFilesFolderSearchWildcard))
- incompleteName = "*" + incompleteName.Substring(1);
+ incompleteName = string.Concat("*", incompleteName.AsSpan(1));
}
incompleteName += "*";
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs
index 92538d554..b1710a30a 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingAPI.cs
@@ -2,6 +2,8 @@
using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions;
using System;
using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
@@ -15,7 +17,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
private const int BufferSize = 4096;
- private static readonly object syncObject = new object();
+ private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
// cached buffer to remove redundant allocations.
private static readonly StringBuilder buffer = new StringBuilder(BufferSize);
@@ -71,17 +73,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
set => EverythingApiDllImport.Everything_SetRegex(value);
}
- ///
- /// Resets this instance.
- ///
- private static void Reset()
- {
- lock (syncObject)
- {
- EverythingApiDllImport.Everything_Reset();
- }
- }
-
///
/// Checks whether the sort option is Fast Sort.
///
@@ -99,24 +90,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
///
/// Searches the specified key word and reset the everything API afterwards
///
- /// The key word.
+ /// Search Criteria
/// when cancelled the current search will stop and exit (and would not reset)
- /// Search Within a parent folder
- /// Search Within sub folder of the parent folder
- /// Sort By
- /// The offset.
- /// The max count.
- ///
- public static IEnumerable SearchAsync(EverythingSearchOption option,
- CancellationToken token)
+ /// An IAsyncEnumerable that will enumerate all results searched by the specific query and option
+ public static async IAsyncEnumerable SearchAsync(EverythingSearchOption option,
+ [EnumeratorCancellation] CancellationToken token)
{
if (option.Offset < 0)
throw new ArgumentOutOfRangeException(nameof(option.Offset), option.Offset, "Offset must be greater than or equal to 0");
if (option.MaxCount < 0)
throw new ArgumentOutOfRangeException(nameof(option.MaxCount), option.MaxCount, "MaxCount must be greater than or equal to 0");
-
- lock (syncObject)
+
+ await _semaphore.WaitAsync(token);
+
+ if (token.IsCancellationRequested)
+ yield break;
+
+ try
{
if (option.Keyword.StartsWith("@"))
{
@@ -140,24 +131,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
EverythingApiDllImport.Everything_SetSort(option.SortOption);
- if (token.IsCancellationRequested)
- {
- return null;
- }
+ if (token.IsCancellationRequested) yield break;
+
if (!EverythingApiDllImport.Everything_QueryW(true))
{
CheckAndThrowExceptionOnError();
- return null;
+ yield break;
}
- var results = new List();
for (var idx = 0; idx < EverythingApiDllImport.Everything_GetNumResults(); ++idx)
{
if (token.IsCancellationRequested)
{
- return null;
+ yield break;
}
EverythingApiDllImport.Everything_GetResultFullPathNameW(idx, buffer, BufferSize);
@@ -170,12 +158,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
ResultType.Volume
};
- results.Add(result);
+ yield return result;
}
-
- Reset();
-
- return results;
+ }
+ finally
+ {
+ EverythingApiDllImport.Everything_Reset();
+ _semaphore.Release();
}
}
@@ -197,6 +186,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
throw new MemoryErrorException();
case StateCode.RegisterClassExError:
throw new RegisterClassExException();
+ case StateCode.OK:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
}
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs
index 3bd4a3c7c..f22542ee2 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs
@@ -1,5 +1,6 @@
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -15,37 +16,36 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
}
- public ValueTask> SearchAsync(string search, CancellationToken token)
+ public IAsyncEnumerable SearchAsync(string search, CancellationToken token)
{
- return ValueTask.FromResult(EverythingApi.SearchAsync(
+ return EverythingApi.SearchAsync(
new EverythingSearchOption(search, Settings.SortOption),
- token));
+ token);
}
- public ValueTask> ContentSearchAsync(string plainSearch,
+ public IAsyncEnumerable ContentSearchAsync(string plainSearch,
string contentSearch, CancellationToken token)
{
if (!Settings.EnableEverythingContentSearch)
{
- return new ValueTask>(new List());
+ return AsyncEnumerable.Empty();
}
- return new ValueTask>(EverythingApi.SearchAsync(
+ return EverythingApi.SearchAsync(
new EverythingSearchOption(
plainSearch,
Settings.SortOption,
true,
contentSearch),
- token));
+ token);
}
- public ValueTask> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
+ public IAsyncEnumerable EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
{
- return new ValueTask>(
- EverythingApi.SearchAsync(
+ return EverythingApi.SearchAsync(
new EverythingSearchOption(search,
Settings.SortOption,
- parentPath: path,
- isRecursive: recursive),
- token));
+ ParentPath: path,
+ IsRecursive: recursive),
+ token);
}
}
}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs
index ef41cf1f0..9d52d35fb 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchOption.cs
@@ -2,34 +2,12 @@
namespace Flow.Launcher.Plugin.Explorer.Search.Everything
{
- public struct EverythingSearchOption
- {
- public EverythingSearchOption(string keyword,
- SortOption sortOption,
- bool isContentSearch = false,
- string contentSearchKeyword = "",
- string parentPath = "",
- bool isRecursive = true,
- int offset = 0,
- int maxCount = 100)
- {
- Keyword = keyword;
- SortOption = sortOption;
- ContentSearchKeyword = contentSearchKeyword;
- IsContentSearch = isContentSearch;
- ParentPath = parentPath;
- IsRecursive = isRecursive;
- Offset = offset;
- MaxCount = maxCount;
- }
- public string Keyword { get; set; }
- public SortOption SortOption { get; set; }
- public string ParentPath { get; set; }
- public bool IsRecursive { get; set; }
-
- public bool IsContentSearch { get; set; }
- public string ContentSearchKeyword { get; set; }
- public int Offset { get;set; }
- public int MaxCount { get; set; }
- }
+ public record struct EverythingSearchOption(string Keyword,
+ SortOption SortOption,
+ bool IsContentSearch = false,
+ string ContentSearchKeyword = "",
+ string ParentPath = "",
+ bool IsRecursive = true,
+ int Offset = 0,
+ int MaxCount = 100);
}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IContentIndexProvider.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IContentIndexProvider.cs
index bd7e14a66..7e2506f9a 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IContentIndexProvider.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IContentIndexProvider.cs
@@ -6,6 +6,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
public interface IContentIndexProvider
{
- public ValueTask> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token);
+ public IAsyncEnumerable ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token = default);
}
}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IIndexProvider.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IIndexProvider.cs
index be40b6a24..dfb07f9c4 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IIndexProvider.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IIndexProvider.cs
@@ -6,6 +6,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
public interface IIndexProvider
{
- public ValueTask> SearchAsync(string search, CancellationToken token);
+ public IAsyncEnumerable SearchAsync(string search, CancellationToken token);
}
}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IPathEnumerable.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IPathEnumerable.cs
index eb82422f4..57425c02b 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/IPathEnumerable.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/IPathEnumerable.cs
@@ -7,6 +7,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
public interface IPathEnumerable
{
- public ValueTask> EnumerateAsync(string path, string search, bool recursive, CancellationToken token);
+ public IAsyncEnumerable EnumerateAsync(string path, string search, bool recursive, CancellationToken token);
}
}
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
index 255d52632..32bd1fd7e 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs
@@ -41,8 +41,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
internal async Task> SearchAsync(Query query, CancellationToken token)
{
- var querySearch = query.Search;
-
var results = new HashSet(PathEqualityComparator.Instance);
// This allows the user to type the below action keywords and see/search the list of quick folder links
@@ -58,35 +56,37 @@ namespace Flow.Launcher.Plugin.Explorer.Search
results.UnionWith(quickAccessLinks);
}
- IEnumerable searchResults;
+ IAsyncEnumerable searchResults;
if (ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword))
{
+ // A backdoor to prevent everything to do content search
if (Settings.ContentIndexProvider is EverythingSearchManager && !Settings.EnableEverythingContentSearch)
{
return EverythingContentSearchResult(query);
}
- searchResults = await Settings.ContentIndexProvider.ContentSearchAsync("", query.Search, token);
+ searchResults = Settings.ContentIndexProvider.ContentSearchAsync("", query.Search, token);
}
else
{
- searchResults = await Settings.IndexProvider.SearchAsync(query.Search, token);
+ searchResults = Settings.IndexProvider.SearchAsync(query.Search, token);
}
+
if (ActionKeywordMatch(query, Settings.ActionKeyword.PathSearchActionKeyword) ||
ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword))
{
results.UnionWith(await PathSearchAsync(query, token).ConfigureAwait(false));
}
- if (((ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) ||
- ActionKeywordMatch(query, Settings.ActionKeyword.SearchActionKeyword)) &&
- querySearch.Length > 0) ||
- ActionKeywordMatch(query, Settings.ActionKeyword.FileContentSearchActionKeyword) &&
- !querySearch.IsLocationPathString())
+ if (searchResults == null)
{
- if (searchResults != null)
- results.UnionWith(searchResults.Select(search => ResultManager.CreateResult(query, search)));
+ return results.ToList();
+ }
+
+ await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false))
+ {
+ results.Add(ResultManager.CreateResult(query, search));
}
return results.ToList();
@@ -131,7 +131,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
};
}
- public async Task> PathSearchAsync(Query query, CancellationToken token = default)
+ private async Task> PathSearchAsync(Query query, CancellationToken token = default)
{
var querySearch = query.Search;
@@ -154,7 +154,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).LocationExists())
return results.ToList();
- var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath);
+ var useIndexSearch = Settings.IndexSearchEngine is Settings.IndexSearchEngineOption.WindowsIndex
+ && UseWindowsIndexForDirectorySearch(locationPath);
results.Add(ResultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch));
@@ -167,10 +168,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search
if (recursiveIndicatorIndex > 0 && Settings.PathEnumerationEngine != Settings.PathEnumerationEngineOption.Direct)
{
directoryResult =
- await Settings.PathEnumerator.EnumerateAsync(query.Search[..recursiveIndicatorIndex],
+ await Settings.PathEnumerator.EnumerateAsync(
+ query.Search[..recursiveIndicatorIndex],
query.Search[(recursiveIndicatorIndex + 1)..],
true,
- token)
+ token).ToListAsync(cancellationToken: token)
.ConfigureAwait(false);
}
@@ -188,10 +190,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search
Title = string.Format(SearchManager.Context.API.GetTranslation(
"plugin_explorer_directoryinfosearch_error"),
e.Message),
- Score = 501,
+ Score = 501,
IcoPath = Constants.ExplorerIconImagePath
});
- directoryResult = new List();
+ directoryResult = Enumerable.Empty();
}
}
@@ -204,25 +206,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
return results.ToList();
}
- public bool IsFileContentSearch(string actionKeyword)
- {
- return actionKeyword == Settings.FileContentSearchActionKeyword;
- }
-
-
- public async Task> TopLevelDirectorySearchBehaviourAsync(
- Func>> windowsIndexSearch,
- Func> directoryInfoClassSearch,
- bool useIndexSearch,
- Query query,
- string querySearchString,
- CancellationToken token)
- {
- if (!useIndexSearch)
- return directoryInfoClassSearch(query, querySearchString, token);
-
- return await windowsIndexSearch(query, querySearchString, token);
- }
+ public static bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword;
+
private bool UseWindowsIndexForDirectorySearch(string locationPath)
{
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs
index 64717b41c..6a6d8cdb8 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndex.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
@@ -19,66 +20,47 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
// Reserved keywords in oleDB
private const string ReservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_;\[\]]+$";
- private static async Task> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, CancellationToken token)
+ private static async IAsyncEnumerable ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, [EnumeratorCancellation] CancellationToken token)
{
- var results = new List();
+ await using var conn = new OleDbConnection(connectionString);
+ await conn.OpenAsync(token);
+ token.ThrowIfCancellationRequested();
- try
+ await using var command = new OleDbCommand(indexQueryString, conn);
+ // Results return as an OleDbDataReader.
+ await using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader;
+ token.ThrowIfCancellationRequested();
+
+ if (dataReaderResults is not { HasRows: true })
+ {
+ yield break;
+ }
+ while (await dataReaderResults.ReadAsync(token))
{
- await using var conn = new OleDbConnection(connectionString);
- await conn.OpenAsync(token);
token.ThrowIfCancellationRequested();
-
- await using var command = new OleDbCommand(indexQueryString, conn);
- // Results return as an OleDbDataReader.
- await using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader;
- token.ThrowIfCancellationRequested();
-
- if (dataReaderResults is { HasRows: true })
+ if (dataReaderResults.GetValue(0) == DBNull.Value || dataReaderResults.GetValue(1) == DBNull.Value)
{
- while (await dataReaderResults.ReadAsync(token))
- {
- token.ThrowIfCancellationRequested();
- if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value)
- {
- // # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path
- var encodedFragmentPath = dataReaderResults
- .GetString(1)
- .Replace("#", "%23", StringComparison.OrdinalIgnoreCase);
-
- var path = new Uri(encodedFragmentPath).LocalPath;
-
- results.Add(new SearchResult()
- {
- FullPath = path,
- Type = dataReaderResults.GetString(2) == "Directory" ? ResultType.Folder : ResultType.File,
- WindowsIndexed = true
- });
- }
- }
+ continue;
}
- }
- catch (OperationCanceledException)
- {
- // return empty result when cancelled
- return results;
- }
- catch (InvalidOperationException e)
- {
- // Internal error from ExecuteReader(): Connection closed.
- LogException("Internal error from ExecuteReader()", e);
- }
- catch (Exception e)
- {
- LogException("General error from performing index search", e);
+ // # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path
+ var encodedFragmentPath = dataReaderResults
+ .GetString(1)
+ .Replace("#", "%23", StringComparison.OrdinalIgnoreCase);
+
+ var path = new Uri(encodedFragmentPath).LocalPath;
+
+ yield return new SearchResult()
+ {
+ FullPath = path,
+ Type = dataReaderResults.GetString(2) == "Directory" ? ResultType.Folder : ResultType.File,
+ WindowsIndexed = true
+ };
}
// Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
- return results;
}
- internal static async ValueTask> WindowsIndexSearchAsync(
- string searchString,
+ internal static IAsyncEnumerable WindowsIndexSearchAsync(string searchString,
Func createQueryHelper,
Func constructQuery,
IEnumerable exclusionList,
@@ -87,12 +69,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
var regexMatch = Regex.Match(searchString, ReservedStringPattern);
if (regexMatch.Success)
- return new();
+ return AsyncEnumerable.Empty();
var constructedQuery = constructQuery(searchString);
- return
- await ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, token);
+ return ExecuteWindowsIndexSearchAsync(constructedQuery, createQueryHelper().ConnectionString, token);
}
private static void RemoveResultsInExclusionList(List results, IReadOnlyList exclusionList, CancellationToken token)
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs
index 05e75c646..2b170afc4 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -13,61 +14,55 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
Settings = settings;
QueryConstructor = new QueryConstructor(Settings);
}
-
-
- private async Task> WindowsIndexFileContentSearchAsync(string querySearchString,
+
+ private IAsyncEnumerable WindowsIndexFileContentSearchAsync(string querySearchString,
CancellationToken token)
{
if (string.IsNullOrEmpty(querySearchString))
- return new List();
+ return AsyncEnumerable.Empty();
- return await WindowsIndex.WindowsIndexSearchAsync(
+ return WindowsIndex.WindowsIndexSearchAsync(
querySearchString,
QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForFileContentSearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
- token).ConfigureAwait(false);
+ token);
}
-
-
- private async Task> WindowsIndexFilesAndFoldersSearchAsync(string querySearchString,
+ private IAsyncEnumerable WindowsIndexFilesAndFoldersSearchAsync(string querySearchString,
CancellationToken token)
{
- return await WindowsIndex.WindowsIndexSearchAsync(
+ return WindowsIndex.WindowsIndexSearchAsync(
querySearchString,
QueryConstructor.CreateQueryHelper,
QueryConstructor.QueryForAllFilesAndFolders,
Settings.IndexSearchExcludedSubdirectoryPaths,
- token).ConfigureAwait(false);
+ token);
}
-
-
- private async Task> WindowsIndexTopLevelFolderSearchAsync(string path,string search,
+
+ private IAsyncEnumerable WindowsIndexTopLevelFolderSearchAsync(string path,string search,
CancellationToken token)
{
var queryConstructor = new QueryConstructor(Settings);
- return await WindowsIndex.WindowsIndexSearchAsync(
+ return WindowsIndex.WindowsIndexSearchAsync(
path,
queryConstructor.CreateQueryHelper,
queryConstructor.QueryForTopLevelDirectorySearch,
Settings.IndexSearchExcludedSubdirectoryPaths,
- token).ConfigureAwait(false);
+ token);
}
- public async ValueTask> SearchAsync(string search, CancellationToken token)
+ public IAsyncEnumerable SearchAsync(string search, CancellationToken token)
{
- return await WindowsIndexFilesAndFoldersSearchAsync(search, token);
+ return WindowsIndexFilesAndFoldersSearchAsync(search, token);
}
- public async ValueTask> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
+ public IAsyncEnumerable ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
{
- return await WindowsIndexFileContentSearchAsync(contentSearch, token);
+ return WindowsIndexFileContentSearchAsync(contentSearch, token);
}
- public async ValueTask> EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
+ public IAsyncEnumerable EnumerateAsync(string path, string search, bool recursive, CancellationToken token)
{
- if(recursive)
- return await WindowsIndexFilesAndFoldersSearchAsync(search, token).ConfigureAwait(false);
- return await WindowsIndexTopLevelFolderSearchAsync(path, search, token);
+ return recursive ? WindowsIndexFilesAndFoldersSearchAsync(search, token) : WindowsIndexTopLevelFolderSearchAsync(path, search, token);
}
}
}
\ No newline at end of file