mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge 7efaaa6d09 into 916b4ff091
This commit is contained in:
commit
994052f3a7
9 changed files with 243 additions and 33 deletions
|
|
@ -39,8 +39,8 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("C:\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("C:\\SomeFolder\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("C:\\", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND directory='file:C:\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("C:\\SomeFolder\\", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND directory='file:C:\\SomeFolder\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
|
||||
{
|
||||
// Given
|
||||
|
|
@ -56,8 +56,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("C:\\SomeFolder", "flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType" +
|
||||
" FROM SystemIndex WHERE directory='file:C:\\SomeFolder'" +
|
||||
[TestCase("C:\\SomeFolder", "flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND directory='file:C:\\SomeFolder'" +
|
||||
" AND (System.FileName LIKE 'flow.launcher.sln%' OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"'))" +
|
||||
$" ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
|
||||
|
|
@ -87,7 +86,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[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 {QueryConstructor.OrderIdentifier}")]
|
||||
$"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033) RANK BY COERCION(ABSOLUTE, 1000)) AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
|
|
@ -104,7 +103,8 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var resultString = queryConstructor.FilesAndFolders(userSearchString);
|
||||
|
||||
// Then
|
||||
ClassicAssert.AreEqual(expectedString, resultString);
|
||||
ClassicAssert.AreEqual(expectedString, resultString, $"Expected string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
|
|
@ -125,8 +125,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[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 {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("some words", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND FREETEXT('some words') AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
internal const string WindowsIndexingOptions = "srchadmin.dll";
|
||||
|
||||
internal const string ExcludedFileTypesSeparator = ",";
|
||||
|
||||
internal static string ExplorerIconImageFullPath
|
||||
=> Directory.GetParent(Assembly.GetExecutingAssembly().Location.ToString()) + "\\" + ExplorerIconImagePath;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -72,14 +73,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
}
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<SearchResult> SearchAsync(string search, [EnumeratorCancellation] CancellationToken token)
|
||||
public async IAsyncEnumerable<SearchResult> SearchAsync(string search, [EnumeratorCancellation] CancellationToken token, IEnumerable<ResultType> allowedResultTypes = null)
|
||||
{
|
||||
await ThrowIfEverythingNotAvailableAsync(token);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
yield break;
|
||||
|
||||
var option = new EverythingSearchOption(search,
|
||||
var searchKeyword = BuildSearchKeyword(search, allowedResultTypes);
|
||||
|
||||
var option = new EverythingSearchOption(searchKeyword,
|
||||
Settings.SortOption,
|
||||
MaxCount: Settings.MaxResult,
|
||||
IsFullPathSearch: Settings.EverythingSearchFullPath,
|
||||
|
|
@ -89,6 +92,61 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
yield return result;
|
||||
}
|
||||
|
||||
private string BuildSearchKeyword(string search, IEnumerable<ResultType> allowedResultTypes)
|
||||
{
|
||||
var filters = new List<string>();
|
||||
|
||||
var typeFilter = BuildTypeFilter(allowedResultTypes);
|
||||
if (!string.IsNullOrEmpty(typeFilter))
|
||||
filters.Add(typeFilter);
|
||||
|
||||
var extensionFilter = BuildExtensionExclusionFilter();
|
||||
if (!string.IsNullOrEmpty(extensionFilter))
|
||||
filters.Add(extensionFilter);
|
||||
|
||||
if (filters.Count == 0)
|
||||
return search;
|
||||
|
||||
var combinedFilters = string.Join(" ", filters);
|
||||
return string.IsNullOrEmpty(search) ? combinedFilters : $"{combinedFilters} {search}";
|
||||
}
|
||||
|
||||
private static string BuildTypeFilter(IEnumerable<ResultType> allowedResultTypes)
|
||||
{
|
||||
if (allowedResultTypes == null)
|
||||
return "";
|
||||
|
||||
var hasFile = allowedResultTypes.Contains(ResultType.File);
|
||||
var hasFolder = allowedResultTypes.Contains(ResultType.Folder);
|
||||
var hasVolume = allowedResultTypes.Contains(ResultType.Volume);
|
||||
|
||||
return (hasFile, hasFolder, hasVolume) switch
|
||||
{
|
||||
(true, false, false) => "file:",
|
||||
(false, true, false) => "folder:",
|
||||
(false, false, true) => "volume:",
|
||||
(true, true, false) => "<file:|folder:>",
|
||||
(true, false, true) => "<file:|volume:>",
|
||||
(false, true, true) => "<folder:|volume:>",
|
||||
_ => "" // No filtering needed when all allowed or unspecified
|
||||
};
|
||||
}
|
||||
|
||||
private string BuildExtensionExclusionFilter()
|
||||
{
|
||||
// Split extensions, remove whitespace, and add dot prefix
|
||||
var extensions = Settings.ExcludedFileTypeList
|
||||
.Where(ext => !string.IsNullOrWhiteSpace(ext))
|
||||
.Select(ext => $"!*.{ext}")
|
||||
.ToArray();
|
||||
|
||||
if (extensions.Length == 0)
|
||||
return "";
|
||||
|
||||
// Everything syntax: !*.ext1 !*.ext2 to exclude these extensions
|
||||
return string.Join(" ", extensions);
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<SearchResult> ContentSearchAsync(string plainSearch, string contentSearch,
|
||||
[EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
|
|
@ -111,7 +169,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
if (token.IsCancellationRequested)
|
||||
yield break;
|
||||
|
||||
var option = new EverythingSearchOption(plainSearch,
|
||||
// Apply excluded file types in content search
|
||||
var searchKeyword = BuildSearchKeyword(plainSearch, new[] { ResultType.File });
|
||||
|
||||
var option = new EverythingSearchOption(searchKeyword,
|
||||
Settings.SortOption,
|
||||
IsContentSearch: true,
|
||||
ContentSearchKeyword: contentSearch,
|
||||
|
|
@ -132,7 +193,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything
|
|||
if (token.IsCancellationRequested)
|
||||
yield break;
|
||||
|
||||
var option = new EverythingSearchOption(search,
|
||||
// Apply excluded file types in path enumeration
|
||||
var searchKeyword = BuildSearchKeyword(search, null);
|
||||
|
||||
var option = new EverythingSearchOption(searchKeyword,
|
||||
Settings.SortOption,
|
||||
ParentPath: path,
|
||||
IsRecursive: recursive,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,18 @@ using System.Threading;
|
|||
|
||||
namespace Flow.Launcher.Plugin.Explorer.Search.IProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality for searching indexed items.
|
||||
/// </summary>
|
||||
public interface IIndexProvider
|
||||
{
|
||||
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Asynchronously searches for items matching the specified search criteria.
|
||||
/// </summary>
|
||||
/// <param name="search">The search query string.</param>
|
||||
/// <param name="token">The cancellation token to cancel the search operation.</param>
|
||||
/// <param name="allowedResultTypes">Optional collection of result types to filter the search results. If null, all result types are included.</param>
|
||||
/// <returns>An asynchronous enumerable of <see cref="SearchResult"/> objects matching the search criteria.</returns>
|
||||
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token, IEnumerable<ResultType> allowedResultTypes = null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
case false
|
||||
when CanUseIndexSearchByActionKeywords(activeActionKeywords):
|
||||
searchResults = Settings.IndexProvider.SearchAsync(query.Search, token);
|
||||
searchResults = Settings.IndexProvider.SearchAsync(query.Search, token, GetAllowedResultTypeByUsedActionKeyword(activeActionKeywords));
|
||||
engineName = Enum.GetName(Settings.IndexSearchEngine);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -148,9 +148,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
{
|
||||
await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false))
|
||||
{
|
||||
// TODO exclude in quick access
|
||||
if (search.Type == ResultType.File && IsExcludedFile(search))
|
||||
continue;
|
||||
|
||||
// TODO: Optimize filtering by action keyword at the provider level to reduce unnecessary searches.
|
||||
// 3. Filter in quick access
|
||||
//
|
||||
if (IsResultTypeFilteredByActionKeyword(search.Type, actions))
|
||||
continue;
|
||||
|
||||
|
|
@ -195,6 +198,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path search logic. Don't apply filtering by file extensions as it's like ls command.
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SearchException"></exception>
|
||||
private async Task<List<Result>> PathSearchAsync(Query query, CancellationToken token = default)
|
||||
{
|
||||
var querySearch = query.Search;
|
||||
|
|
@ -291,7 +301,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
|
||||
private bool IsExcludedFile(SearchResult result)
|
||||
{
|
||||
string[] excludedFileTypes = Settings.ExcludedFileTypes.Split([','], StringSplitOptions.RemoveEmptyEntries);
|
||||
// TODO may remove this function
|
||||
string[] excludedFileTypes = Settings.ExcludedFileTypes.Split([Constants.ExcludedFileTypesSeparator], StringSplitOptions.RemoveEmptyEntries);
|
||||
string fileExtension = Path.GetExtension(result.FullPath).TrimStart('.');
|
||||
|
||||
return excludedFileTypes.Contains(fileExtension, StringComparer.OrdinalIgnoreCase);
|
||||
|
|
@ -326,6 +337,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search
|
|||
return true;
|
||||
}
|
||||
|
||||
private List<ResultType> GetAllowedResultTypeByUsedActionKeyword(Dictionary<ActionKeyword, string> activeActionKeywords)
|
||||
{
|
||||
List<ActionKeyword> activeKeywords = activeActionKeywords.Keys.ToList();
|
||||
var allowedResultTypes = new List<ResultType>();
|
||||
foreach (var actionKeyword in activeKeywords)
|
||||
{
|
||||
if (_allowedTypesByActionKeyword.TryGetValue(actionKeyword, out var resultTypes))
|
||||
{
|
||||
allowedResultTypes.AddRange(resultTypes);
|
||||
}
|
||||
}
|
||||
return allowedResultTypes.Distinct().ToList();
|
||||
}
|
||||
|
||||
private bool CanUseIndexSearchByActionKeywords(Dictionary<ActionKeyword, string> actions)
|
||||
{
|
||||
var keysToUseIndexSearch = new[]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Search.Interop;
|
||||
|
||||
|
|
@ -32,7 +34,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
baseQuery.QueryContentProperties = "System.FileName";
|
||||
|
||||
// Set sorting order
|
||||
//baseQuery.QuerySorting = "System.ItemType DESC";
|
||||
baseQuery.QuerySorting = OrderIdentifier;
|
||||
|
||||
return baseQuery;
|
||||
}
|
||||
|
|
@ -60,21 +62,21 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
///</summary>
|
||||
public string Directory(ReadOnlySpan<char> path, ReadOnlySpan<char> searchString = default, bool recursive = false)
|
||||
{
|
||||
var queryConstraint = searchString.IsWhiteSpace() ? "" : $"AND (System.FileName LIKE '{searchString}%' OR CONTAINS(System.FileName,'\"{searchString}*\"'))";
|
||||
var queryConstraint = searchString.IsWhiteSpace() ? "" : $" AND (System.FileName LIKE '{searchString}%' OR CONTAINS(System.FileName,'\"{searchString}*\"'))";
|
||||
|
||||
var scopeConstraint = recursive
|
||||
? RecursiveDirectoryConstraint(path)
|
||||
: TopLevelDirectoryConstraint(path);
|
||||
|
||||
var query = $"SELECT TOP {Settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {scopeConstraint} {queryConstraint} ORDER BY {OrderIdentifier}";
|
||||
|
||||
return query;
|
||||
var baseQueryHelper = CreateBaseQuery();
|
||||
baseQueryHelper.QueryWhereRestrictions = $"AND {scopeConstraint}{queryConstraint}";
|
||||
return baseQueryHelper.GenerateSQLFromUserQuery("*");
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// Search will be performed on all folders and files based on user's search keywords.
|
||||
///</summary>
|
||||
public string FilesAndFolders(ReadOnlySpan<char> userSearchString)
|
||||
public string FilesAndFolders(ReadOnlySpan<char> userSearchString, IEnumerable<ResultType> allowedResultTypes = null)
|
||||
{
|
||||
if (userSearchString.IsWhiteSpace())
|
||||
userSearchString = "*";
|
||||
|
|
@ -82,8 +84,69 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
// Remove any special characters that might cause issues with the query
|
||||
var replacedSearchString = ReplaceSpecialCharacterWithTwoSideWhiteSpace(userSearchString);
|
||||
|
||||
// Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause
|
||||
return $"{CreateBaseQuery().GenerateSQLFromUserQuery(replacedSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {OrderIdentifier}";
|
||||
var constraints = new List<string>
|
||||
{
|
||||
RestrictionsForAllFilesAndFoldersSearch
|
||||
};
|
||||
|
||||
var typeConstraint = BuildTypeFilterConstraint(allowedResultTypes);
|
||||
if (!string.IsNullOrEmpty(typeConstraint))
|
||||
constraints.Add(typeConstraint);
|
||||
|
||||
var extensionConstraint = BuildExtensionExclusionConstraint();
|
||||
if (!string.IsNullOrEmpty(extensionConstraint))
|
||||
constraints.Add(extensionConstraint);
|
||||
|
||||
var queryHelper = CreateBaseQuery();
|
||||
queryHelper.QueryWhereRestrictions = $"AND {string.Join(" AND ", constraints)}";
|
||||
return queryHelper.GenerateSQLFromUserQuery(replacedSearchString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build WHERE clause constraint to filter by result types (File, Folder, Volume).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// System.ItemType values:
|
||||
/// - ".directory" for folders
|
||||
/// - Specific file extensions (e.g., ".txt", ".pdf") for files
|
||||
/// - Drive volumes are identified by their path structure
|
||||
/// </remarks>
|
||||
private static string BuildTypeFilterConstraint(IEnumerable<ResultType> allowedResultTypes)
|
||||
{
|
||||
if (allowedResultTypes == null)
|
||||
return null;
|
||||
|
||||
var typesList = allowedResultTypes as IList<ResultType> ?? allowedResultTypes.ToList();
|
||||
var hasFile = typesList.Contains(ResultType.File);
|
||||
var hasFolder = typesList.Contains(ResultType.Folder) || typesList.Contains(ResultType.Volume); // Folder and volume are merged in Folder action keyword so treat them as same
|
||||
|
||||
// No filtering needed if empty or all types are allowed
|
||||
if (hasFile && hasFolder || !hasFile && !hasFolder)
|
||||
return null;
|
||||
|
||||
if (hasFolder)
|
||||
return "System.ItemType = '.directory'";
|
||||
|
||||
if (hasFile)
|
||||
return "System.ItemType <> '.directory'";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build WHERE clause constraint to exclude specific file extensions.
|
||||
/// </summary>
|
||||
/// <param name="excludedFileTypes">Comma or semicolon separated file extensions without dots (e.g., "queryHelper,log,bak")</param>
|
||||
private string BuildExtensionExclusionConstraint()
|
||||
{
|
||||
var extensions = Settings.ExcludedFileTypeList
|
||||
.Select(ext => $"System.FileExtension NOT LIKE '.{ext}'")
|
||||
.ToArray();
|
||||
|
||||
if (extensions.Length == 0)
|
||||
return "";
|
||||
|
||||
return string.Join(" AND ", extensions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -132,10 +195,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
///</summary>
|
||||
public string FileContent(ReadOnlySpan<char> userSearchString)
|
||||
{
|
||||
string query =
|
||||
$"SELECT TOP {Settings.MaxResult} {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE {RestrictionsForFileContentSearch(userSearchString)} AND {RestrictionsForAllFilesAndFoldersSearch} ORDER BY {OrderIdentifier}";
|
||||
var constraints = new List<string>
|
||||
{
|
||||
RestrictionsForFileContentSearch(userSearchString),
|
||||
RestrictionsForAllFilesAndFoldersSearch
|
||||
};
|
||||
|
||||
return query;
|
||||
var extensionConstraint = BuildExtensionExclusionConstraint();
|
||||
if (!string.IsNullOrEmpty(extensionConstraint))
|
||||
constraints.Add(extensionConstraint);
|
||||
|
||||
var queryHelper = CreateBaseQuery();
|
||||
queryHelper.QueryWhereRestrictions = $"AND {string.Join(" AND ", constraints)}";
|
||||
return queryHelper.GenerateSQLFromUserQuery("*");
|
||||
}
|
||||
|
||||
///<summary>
|
||||
|
|
|
|||
|
|
@ -45,13 +45,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
|
||||
private IAsyncEnumerable<SearchResult> WindowsIndexFilesAndFoldersSearchAsync(
|
||||
ReadOnlySpan<char> querySearchString,
|
||||
IEnumerable<ResultType> allowedResultTypes = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
return WindowsIndex.WindowsIndexSearchAsync(
|
||||
QueryConstructor.CreateQueryHelper().ConnectionString,
|
||||
QueryConstructor.FilesAndFolders(querySearchString),
|
||||
QueryConstructor.FilesAndFolders(querySearchString, allowedResultTypes),
|
||||
token);
|
||||
}
|
||||
catch (COMException)
|
||||
|
|
@ -83,9 +84,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
|
|||
}
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token)
|
||||
public IAsyncEnumerable<SearchResult> SearchAsync(string search, CancellationToken token, IEnumerable<ResultType> allowedResultTypes = null)
|
||||
{
|
||||
return WindowsIndexFilesAndFoldersSearchAsync(search, token: token);
|
||||
return WindowsIndexFilesAndFoldersSearchAsync(search, allowedResultTypes, token);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<SearchResult> ContentSearchAsync(string plainSearch, string contentSearch, CancellationToken token)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Flow.Launcher.Plugin.Explorer.Search;
|
||||
using Flow.Launcher.Plugin.Explorer.Search.Everything;
|
||||
|
|
@ -25,7 +26,37 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
|
||||
public string ShellPath { get; set; } = "cmd";
|
||||
|
||||
public string ExcludedFileTypes { get; set; } = "";
|
||||
[JsonIgnore]
|
||||
private string _excludedFileTypes = "";
|
||||
/// <summary>
|
||||
/// File extensions, without dot prefix separated by comma.
|
||||
/// </summary>
|
||||
public string ExcludedFileTypes
|
||||
{
|
||||
get => _excludedFileTypes;
|
||||
set
|
||||
{
|
||||
if (_excludedFileTypes == value) return;
|
||||
_excludedFileTypes = value;
|
||||
_excludedFileTypeList = ExcludedFileTypes.Split(Constants.ExcludedFileTypesSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private string[] _excludedFileTypeList = null;
|
||||
|
||||
[JsonIgnore]
|
||||
public string[] ExcludedFileTypeList
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_excludedFileTypeList == null)
|
||||
{
|
||||
_excludedFileTypeList = ExcludedFileTypes.Split(Constants.ExcludedFileTypesSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToArray();
|
||||
}
|
||||
return _excludedFileTypeList;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseLocationAsWorkingDir { get; set; } = false;
|
||||
|
||||
|
|
@ -82,12 +113,16 @@ namespace Flow.Launcher.Plugin.Explorer
|
|||
|
||||
public string PreviewPanelTimeFormat { get; set; } = "HH:mm";
|
||||
|
||||
private EverythingSearchManager _everythingManagerInstance;
|
||||
private WindowsIndexSearchManager _windowsIndexSearchManager;
|
||||
|
||||
#region SearchEngine
|
||||
|
||||
[JsonIgnore]
|
||||
private EverythingSearchManager _everythingManagerInstance;
|
||||
[JsonIgnore]
|
||||
private WindowsIndexSearchManager _windowsIndexSearchManager;
|
||||
[JsonIgnore]
|
||||
private EverythingSearchManager EverythingManagerInstance => _everythingManagerInstance ??= new EverythingSearchManager(this);
|
||||
[JsonIgnore]
|
||||
private WindowsIndexSearchManager WindowsIndexSearchManager => _windowsIndexSearchManager ??= new WindowsIndexSearchManager(this);
|
||||
|
||||
public IndexSearchEngineOption IndexSearchEngine { get; set; } = IndexSearchEngineOption.WindowsIndex;
|
||||
|
|
|
|||
|
|
@ -574,6 +574,8 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
|
|||
get => Settings.ExcludedFileTypes;
|
||||
set
|
||||
{
|
||||
if (value == Settings.ExcludedFileTypes)
|
||||
return;
|
||||
// remove spaces and dots from the string before saving
|
||||
string sanitized = string.IsNullOrEmpty(value) ? "" : value.Replace(" ", "").Replace(".", "");
|
||||
Settings.ExcludedFileTypes = sanitized;
|
||||
|
|
|
|||
Loading…
Reference in a new issue