diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 5cb3a171a67..bd8d32ff511 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -1,7 +1,9 @@ using System; using System.Diagnostics; using System.IO; +#pragma warning disable IDE0005 using System.Windows; +#pragma warning restore IDE0005 namespace Flow.Launcher.Plugin.SharedCommands { @@ -206,22 +208,16 @@ public static bool IsLocationPathString(this string querySearchString) /// public static string GetPreviousExistingDirectory(Func locationExists, string path) { - var previousDirectoryPath = ""; var index = path.LastIndexOf('\\'); if (index > 0 && index < (path.Length - 1)) { - previousDirectoryPath = path.Substring(0, index + 1); - if (!locationExists(previousDirectoryPath)) - { - return ""; - } + string previousDirectoryPath = path.Substring(0, index + 1); + return locationExists(previousDirectoryPath) ? previousDirectoryPath : ""; } else { return ""; } - - return previousDirectoryPath; } /// @@ -241,5 +237,33 @@ public static string ReturnPreviousDirectoryIfIncompleteString(string path) return path; } + + /// + /// Returns if contains . + /// From https://stackoverflow.com/a/66877016 + /// + /// Parent path + /// Sub path + /// If , when and are equal, returns + /// + public static bool PathContains(string parentPath, string subPath, bool allowEqual = false) + { + var rel = Path.GetRelativePath(parentPath.EnsureTrailingSlash(), subPath); + return (rel != "." || allowEqual) + && rel != ".." + && !rel.StartsWith("../") + && !rel.StartsWith(@"..\") + && !Path.IsPathRooted(rel); + } + + /// + /// Returns path ended with "\" + /// + /// + /// + public static string EnsureTrailingSlash(this string path) + { + return path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; + } } } diff --git a/Flow.Launcher.Test/FilesFoldersTest.cs b/Flow.Launcher.Test/FilesFoldersTest.cs new file mode 100644 index 00000000000..d1682605377 --- /dev/null +++ b/Flow.Launcher.Test/FilesFoldersTest.cs @@ -0,0 +1,53 @@ +using Flow.Launcher.Plugin.SharedCommands; +using NUnit.Framework; + +namespace Flow.Launcher.Test +{ + [TestFixture] + + public class FilesFoldersTest + { + // Testcases from https://stackoverflow.com/a/31941905/20703207 + // Disk + [TestCase(@"c:", @"c:\foo", true)] + [TestCase(@"c:\", @"c:\foo", true)] + // Slash + [TestCase(@"c:\foo\bar\", @"c:\foo\", false)] + [TestCase(@"c:\foo\bar", @"c:\foo\", false)] + [TestCase(@"c:\foo", @"c:\foo\bar", true)] + [TestCase(@"c:\foo\", @"c:\foo\bar", true)] + // File + [TestCase(@"c:\foo", @"c:\foo\a.txt", true)] + [TestCase(@"c:\foo", @"c:/foo/a.txt", true)] + [TestCase(@"c:\FOO\a.txt", @"c:\foo", false)] + [TestCase(@"c:\foo\a.txt", @"c:\foo\", false)] + [TestCase(@"c:\foobar\a.txt", @"c:\foo", false)] + [TestCase(@"c:\foobar\a.txt", @"c:\foo\", false)] + [TestCase(@"c:\foo\", @"c:\foo.txt", false)] + // Prefix + [TestCase(@"c:\foo", @"c:\foobar", false)] + [TestCase(@"C:\Program", @"C:\Program Files\", false)] + [TestCase(@"c:\foobar", @"c:\foo\a.txt", false)] + [TestCase(@"c:\foobar\", @"c:\foo\a.txt", false)] + // Edge case + [TestCase(@"c:\foo", @"c:\foo\..\bar\baz", false)] + [TestCase(@"c:\bar", @"c:\foo\..\bar\baz", true)] + [TestCase(@"c:\barr", @"c:\foo\..\bar\baz", false)] + // Equality + [TestCase(@"c:\foo", @"c:\foo", false)] + [TestCase(@"c:\foo\", @"c:\foo", false)] + [TestCase(@"c:\foo", @"c:\foo\", false)] + public void GivenTwoPaths_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult) + { + Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path)); + } + + [TestCase(@"c:\foo", @"c:\foo", true)] + [TestCase(@"c:\foo\", @"c:\foo", true)] + [TestCase(@"c:\foo", @"c:\foo\", true)] + public void GivenTwoPathsAreTheSame_WhenCheckPathContains_ThenShouldBeTrue(string parentPath, string path, bool expectedResult) + { + Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, true)); + } + } +} diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 36f0294a93e..e9d37433f4e 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -7,9 +7,11 @@ using NUnit.Framework; using System; using System.Collections.Generic; +using System.IO; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; +using static Flow.Launcher.Plugin.Explorer.Search.SearchManager; namespace Flow.Launcher.Test.Plugins { @@ -176,7 +178,7 @@ public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileConte 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, @@ -193,6 +195,7 @@ public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileConte [TestCase(@"c:\>*", true)] [TestCase(@"c:\>", true)] [TestCase(@"c:\SomeLocation\SomeOtherLocation\>", true)] + [TestCase(@"c:\SomeLocation\SomeOtherLocation", true)] public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult) { // When, Given @@ -393,5 +396,68 @@ public void GivenQueryWithFileTypeResult_WhenGetAutoComplete_ThenResultShouldBeE // Then Assert.AreEqual(result, expectedResult); } + + [TestCase(@"c:\foo", @"c:\foo", true)] + [TestCase(@"C:\Foo\", @"c:\foo\", true)] + [TestCase(@"c:\foo", @"c:\foo\", false)] + public void GivenTwoPaths_WhenCompared_ThenShouldBeExpectedSameOrDifferent(string path1, string path2, bool expectedResult) + { + // Given + var comparator = PathEqualityComparator.Instance; + var result1 = new Result + { + Title = Path.GetFileName(path1), + SubTitle = path1 + }; + var result2 = new Result + { + Title = Path.GetFileName(path2), + SubTitle = path2 + }; + + // When, Then + Assert.AreEqual(expectedResult, comparator.Equals(result1, result2)); + } + + [TestCase(@"c:\foo\", @"c:\foo\")] + [TestCase(@"C:\Foo\", @"c:\foo\")] + public void GivenTwoPaths_WhenComparedHasCode_ThenShouldBeSame(string path1, string path2) + { + // Given + var comparator = PathEqualityComparator.Instance; + var result1 = new Result + { + Title = Path.GetFileName(path1), + SubTitle = path1 + }; + var result2 = new Result + { + Title = Path.GetFileName(path2), + SubTitle = path2 + }; + + var hash1 = comparator.GetHashCode(result1); + var hash2 = comparator.GetHashCode(result2); + + // When, Then + Assert.IsTrue(hash1 == hash2); + } + + [TestCase(@"%appdata%", true)] + [TestCase(@"%appdata%\123", true)] + [TestCase(@"c:\foo %appdata%\", false)] + [TestCase(@"c:\users\%USERNAME%\downloads", true)] + [TestCase(@"c:\downloads", false)] + [TestCase(@"%", false)] + [TestCase(@"%%", false)] + [TestCase(@"%bla%blabla%", false)] + public void GivenPath_WhenHavingEnvironmentVariableOrNot_ThenShouldBeExpected(string path, bool expectedResult) + { + // When + var result = EnvironmentVariables.HasEnvironmentVar(path); + + // Then + Assert.AreEqual(result, expectedResult); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Exceptions/EngineNotAvailableException.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Exceptions/EngineNotAvailableException.cs index 1a48892f597..a6315fb4b7f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Exceptions/EngineNotAvailableException.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Exceptions/EngineNotAvailableException.cs @@ -3,8 +3,6 @@ using System; using System.Threading.Tasks; using System.Windows; -using Flow.Launcher.Plugin.Explorer.Search.IProvider; -using JetBrains.Annotations; namespace Flow.Launcher.Plugin.Explorer.Exceptions; @@ -20,7 +18,7 @@ public EngineNotAvailableException( string engineName, string resolution, string message, - Func> action = null) : base(message) + Func>? action = null) : base(message) { EngineName = engineName; Resolution = resolution; @@ -40,6 +38,23 @@ public EngineNotAvailableException( EngineName = engineName; Resolution = resolution; } + + public EngineNotAvailableException( + string engineName, + string resolution, + string message, + string errorIconPath, + Func>? action = null) : base(message) + { + EngineName = engineName; + Resolution = resolution; + ErrorIcon = errorIconPath; + Action = action ?? (_ => + { + Clipboard.SetDataObject(this.ToString()); + return ValueTask.FromResult(true); + }); + } public override string ToString() { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index 595ac36109c..e526fb85a1f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -1,70 +1,75 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.IO; -using System.Text; +using System.Linq; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.Explorer.Search { public static class EnvironmentVariables { + private static Dictionary _envStringPaths = null; + private static Dictionary EnvStringPaths + { + get + { + if (_envStringPaths == null) + { + LoadEnvironmentStringPaths(); + } + return _envStringPaths; + } + } + internal static bool IsEnvironmentVariableSearch(string search) { - return search.StartsWith("%") + return search.StartsWith("%") && search != "%%" - && !search.Contains("\\") && - LoadEnvironmentStringPaths().Count > 0; + && !search.Contains('\\') + && EnvStringPaths.Count > 0; } - internal static Dictionary LoadEnvironmentStringPaths() + public static bool HasEnvironmentVar(string search) { - var envStringPaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + // "c:\foo %appdata%\" returns false + var splited = search.Split(Path.DirectorySeparatorChar); + return splited.Any(dir => dir.StartsWith('%') && + dir.EndsWith('%') && + dir.Length > 2 && + dir.Split('%').Length == 3); + } + + private static void LoadEnvironmentStringPaths() + { + _envStringPaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + var homedrive = Environment.GetEnvironmentVariable("HOMEDRIVE")?.EnsureTrailingSlash() ?? "C:\\"; foreach (DictionaryEntry special in Environment.GetEnvironmentVariables()) { var path = special.Value.ToString(); - if (Directory.Exists(path)) - { - // we add a trailing slash to the path to make sure drive paths become valid absolute paths. - // for example, if %systemdrive% is C: we turn it to C:\ - path = path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; + // we add a trailing slash to the path to make sure drive paths become valid absolute paths. + // for example, if %systemdrive% is C: we turn it to C:\ + path = path.EnsureTrailingSlash(); - // if we don't have an absolute path, we use Path.GetFullPath to get one. - // for example, if %homepath% is \Users\John we turn it to C:\Users\John - path = Path.IsPathFullyQualified(path) ? path : Path.GetFullPath(path); + // if we don't have an absolute path, we use Path.GetFullPath to get one. + // for example, if %homepath% is \Users\John we turn it to C:\Users\John + // Add basepath for GetFullPath() to parse %HOMEPATH% correctly + path = Path.IsPathFullyQualified(path) ? path : Path.GetFullPath(path, homedrive); + if (Directory.Exists(path)) + { // Variables are returned with a mixture of all upper/lower case. - // Call ToLower() to make the results look consistent - envStringPaths.Add(special.Key.ToString().ToLower(), path); + // Call ToUpper() to make the results look consistent + _envStringPaths.Add(special.Key.ToString().ToUpper(), path); } } - - return envStringPaths; - } - - internal static string TranslateEnvironmentVariablePath(string environmentVariablePath) - { - var envStringPaths = LoadEnvironmentStringPaths(); - var splitSearch = environmentVariablePath.Substring(1).Split("%"); - var exactEnvStringPath = splitSearch[0]; - - // if there are more than 2 % characters in the query, don't bother - if (splitSearch.Length == 2 && envStringPaths.ContainsKey(exactEnvStringPath)) - { - var queryPartToReplace = $"%{exactEnvStringPath}%"; - var expandedPath = envStringPaths[exactEnvStringPath]; - // replace the %envstring% part of the query with its expanded equivalent - return environmentVariablePath.Replace(queryPartToReplace, expandedPath); - } - - return environmentVariablePath; } internal static List GetEnvironmentStringPathSuggestions(string querySearch, Query query, PluginInitContext context) { var results = new List(); - var environmentVariables = LoadEnvironmentStringPaths(); var search = querySearch; if (querySearch.EndsWith("%") && search.Length > 1) @@ -72,12 +77,12 @@ internal static List GetEnvironmentStringPathSuggestions(string querySea // query starts and ends with a %, find an exact match from env-string paths search = querySearch.Substring(1, search.Length - 2); - if (environmentVariables.ContainsKey(search)) + if (EnvStringPaths.ContainsKey(search)) { - var expandedPath = environmentVariables[search]; - + var expandedPath = EnvStringPaths[search]; + results.Add(ResultManager.CreateFolderResult($"%{search}%", expandedPath, expandedPath, query)); - + return results; } } @@ -90,8 +95,8 @@ internal static List GetEnvironmentStringPathSuggestions(string querySea { search = search.Substring(1); } - - foreach (var p in environmentVariables) + + foreach (var p in EnvStringPaths) { if (p.Key.StartsWith(search, StringComparison.InvariantCultureIgnoreCase)) { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs index ffd22d9f5eb..4bb8a0cddeb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs @@ -27,20 +27,16 @@ private async ValueTask ThrowIfEverythingNotAvailableAsync(CancellationToken tok Enum.GetName(Settings.IndexSearchEngineOption.Everything)!, Main.Context.API.GetTranslation("flowlauncher_plugin_everything_click_to_launch_or_install"), Main.Context.API.GetTranslation("flowlauncher_plugin_everything_is_not_running"), - ClickToInstallEverythingAsync) - { - ErrorIcon = Constants.EverythingErrorImagePath - }; + Constants.EverythingErrorImagePath, + ClickToInstallEverythingAsync); } catch (DllNotFoundException) { throw new EngineNotAvailableException( Enum.GetName(Settings.IndexSearchEngineOption.Everything)!, "Please check whether your system is x86 or x64", - Main.Context.API.GetTranslation("flowlauncher_plugin_everything_sdk_issue")) - { - ErrorIcon = Constants.GeneralSearchErrorImagePath - }; + Constants.GeneralSearchErrorImagePath, + Main.Context.API.GetTranslation("flowlauncher_plugin_everything_sdk_issue")); } } private async ValueTask ClickToInstallEverythingAsync(ActionContext _) @@ -72,16 +68,14 @@ public async IAsyncEnumerable ContentSearchAsync(string plainSearc if (!Settings.EnableEverythingContentSearch) { throw new EngineNotAvailableException(Enum.GetName(Settings.IndexSearchEngineOption.Everything)!, - "Click to Enable Everything Content Search (only applicable to Everything 1.5+ with indexed content)", - "Everything Content Search is not enabled.", + Main.Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search"), + Main.Context.API.GetTranslation("flowlauncher_plugin_everything_enable_content_search_tips"), + Constants.EverythingErrorImagePath, _ => { Settings.EnableEverythingContentSearch = true; return ValueTask.FromResult(true); - }) - { - ErrorIcon = Constants.EverythingErrorImagePath - }; + }); } if (token.IsCancellationRequested) yield break; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 62ad7160801..ed4f39735bd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -71,7 +71,7 @@ internal static Result CreateFolderResult(string title, string subtitle, string { Title = title, IcoPath = path, - SubTitle = Path.GetDirectoryName(path), + SubTitle = subtitle, AutoCompleteText = GetAutoCompleteText(title, query, path, ResultType.Folder), TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData, CopyText = path, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 93a81f9470c..51c4c3d9d54 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -13,9 +13,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search { public class SearchManager { - internal static PluginInitContext Context; + internal PluginInitContext Context; - internal static Settings Settings; + internal Settings Settings; public SearchManager(Settings settings, PluginInitContext context) { @@ -23,19 +23,23 @@ public SearchManager(Settings settings, PluginInitContext context) Settings = settings; } - private class PathEqualityComparator : IEqualityComparer + /// + /// Note: A path that ends with "\" and one that doesn't will not be regarded as equal. + /// + public class PathEqualityComparator : IEqualityComparer { private static PathEqualityComparator instance; public static PathEqualityComparator Instance => instance ??= new PathEqualityComparator(); public bool Equals(Result x, Result y) { - return x.Title == y.Title && x.SubTitle == y.SubTitle; + return x.Title.Equals(y.Title, StringComparison.OrdinalIgnoreCase) + && string.Equals(x.SubTitle, y.SubTitle, StringComparison.OrdinalIgnoreCase); } public int GetHashCode(Result obj) { - return HashCode.Combine(obj.Title.GetHashCode(), obj.SubTitle?.GetHashCode() ?? 0); + return HashCode.Combine(obj.Title.ToLowerInvariant(), obj.SubTitle?.ToLowerInvariant() ?? ""); } } @@ -105,19 +109,21 @@ when ActionKeywordMatch(query, Settings.ActionKeyword.IndexSearchActionKeyword) await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false)) results.Add(ResultManager.CreateResult(query, search)); } + catch (OperationCanceledException) + { + return new List(); + } + catch (EngineNotAvailableException) + { + throw; + } catch (Exception e) { - if (e is OperationCanceledException) - return results.ToList(); - - if (e is EngineNotAvailableException) - throw; - throw new SearchException(engineName, e.Message, e); } - + results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any( - excludedPath => r.SubTitle.StartsWith(excludedPath.Path, StringComparison.OrdinalIgnoreCase))); + excludedPath => FilesFolders.PathContains(excludedPath.Path, r.SubTitle))); return results.ToList(); } @@ -142,7 +148,7 @@ private bool ActionKeywordMatch(Query query, Settings.ActionKeyword allowedActio }; } - private static List EverythingContentSearchResult(Query query) + private List EverythingContentSearchResult(Query query) { return new List() { @@ -167,18 +173,12 @@ private async Task> PathSearchAsync(Query query, CancellationToken var results = new HashSet(PathEqualityComparator.Instance); - var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch); - - if (isEnvironmentVariable) + if (EnvironmentVariables.IsEnvironmentVariableSearch(querySearch)) return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, Context); - // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ - var isEnvironmentVariablePath = querySearch[1..].Contains("%\\"); - - var locationPath = querySearch; - - if (isEnvironmentVariablePath) - locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath); + // Query is a location path with a full environment variable, eg. %appdata%\somefolder\, c:\users\%USERNAME%\downloads + var needToExpand = EnvironmentVariables.HasEnvironmentVar(querySearch); + var locationPath = needToExpand ? Environment.ExpandEnvironmentVariables(querySearch) : querySearch; // Check that actual location exists, otherwise directory search will throw directory not found exception if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).LocationExists()) @@ -234,7 +234,7 @@ private async Task> PathSearchAsync(Query query, CancellationToken return results.ToList(); } - public static bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword; + public bool IsFileContentSearch(string actionKeyword) => actionKeyword == Settings.FileContentSearchActionKeyword; private bool UseWindowsIndexForDirectorySearch(string locationPath) @@ -245,10 +245,10 @@ private bool UseWindowsIndexForDirectorySearch(string locationPath) x => FilesFolders.ReturnPreviousDirectoryIfIncompleteString(pathToDirectory).StartsWith(x.Path, StringComparison.OrdinalIgnoreCase)) && WindowsIndex.WindowsIndex.PathIsIndexed(pathToDirectory); } - + internal static bool IsEnvironmentVariableSearch(string search) { - return search.StartsWith("%") + return search.StartsWith("%") && search != "%%" && !search.Contains('\\'); } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs index abb50849d16..c3a7d9e9123 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs @@ -97,27 +97,25 @@ public IAsyncEnumerable EnumerateAsync(string path, string search, private IAsyncEnumerable HandledEngineNotAvailableExceptionAsync() { - if (!SearchManager.Settings.WarnWindowsSearchServiceOff) + if (!Settings.WarnWindowsSearchServiceOff) return AsyncEnumerable.Empty(); - var api = SearchManager.Context.API; + var api = Main.Context.API; throw new EngineNotAvailableException( "Windows Index", api.GetTranslation("plugin_explorer_windowsSearchServiceFix"), api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"), + Constants.WindowsIndexErrorImagePath, c => { - SearchManager.Settings.WarnWindowsSearchServiceOff = false; + Settings.WarnWindowsSearchServiceOff = false; // Clears the warning message so user is not mistaken that it has not worked api.ChangeQuery(string.Empty); return ValueTask.FromResult(false); - }) - { - ErrorIcon = Constants.WindowsIndexErrorImagePath - }; + }); } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f678b6bc89c..e0079967ba3 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -470,8 +470,8 @@ private static IEnumerable PATHPrograms(string[] suffixes, string[] proto } var paths = pathEnv.Split(";", StringSplitOptions.RemoveEmptyEntries).DistinctBy(p => p.ToLowerInvariant()); - - var toFilter = paths.Where(x => commonParents.All(parent => !IsSubPathOf(x, parent))) + + var toFilter = paths.Where(x => commonParents.All(parent => !FilesFolders.PathContains(parent, x))) .AsParallel() .SelectMany(p => EnumerateProgramsInDir(p, suffixes, recursive: false)); @@ -763,17 +763,6 @@ public static void Dispose() } } - // https://stackoverflow.com/a/66877016 - private static bool IsSubPathOf(string subPath, string basePath) - { - var rel = Path.GetRelativePath(basePath, subPath); - return rel != "." - && rel != ".." - && !rel.StartsWith("../") - && !rel.StartsWith(@"..\") - && !Path.IsPathRooted(rel); - } - private static List GetCommonParents(IEnumerable programSources) { // To avoid unnecessary io @@ -785,8 +774,7 @@ private static List GetCommonParents(IEnumerable programS HashSet parents = group.ToHashSet(); foreach (var source in group) { - if (parents.Any(p => IsSubPathOf(source.Location, p.Location) && - source != p)) + if (parents.Any(p => FilesFolders.PathContains(p.Location, source.Location))) { parents.Remove(source); }