From 2e4127c63b0de314f155c19e154a30c6c1294cba Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 00:37:05 +0800 Subject: [PATCH 01/26] Catch specifc exception --- .../Search/SearchManager.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 93a81f947..b3c118fca 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -105,14 +105,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search await foreach (var search in searchResults.WithCancellation(token).ConfigureAwait(false)) results.Add(ResultManager.CreateResult(query, search)); } + catch (OperationCanceledException) + { + return results.ToList(); + } + 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); } From 6efa9d1731fee3de894566eb558c0f92111cdaeb Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 00:40:54 +0800 Subject: [PATCH 02/26] Only expand environment var when path starts with % --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index b3c118fca..45ba690d3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -175,7 +175,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search 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 isEnvironmentVariablePath = querySearch[0] == '%' && querySearch[1..].Contains("%\\"); var locationPath = querySearch; From 983f0aafc4267fa64b9469d72710532aa9b027fd Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 00:49:24 +0800 Subject: [PATCH 03/26] Use a static dictionary for environment vars --- .../Search/EnvironmentVariables.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index 595ac3610..fae5f59cd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.IO; @@ -8,12 +8,25 @@ namespace Flow.Launcher.Plugin.Explorer.Search { public static class EnvironmentVariables { + private static Dictionary _envStringPaths = null; + private static Dictionary EnvStringPaths + { + get + { + if (_envStringPaths == null) + { + _envStringPaths = LoadEnvironmentStringPaths(); + } + return _envStringPaths; + } + } + internal static bool IsEnvironmentVariableSearch(string search) { return search.StartsWith("%") && search != "%%" - && !search.Contains("\\") && - LoadEnvironmentStringPaths().Count > 0; + && !search.Contains("\\") + && EnvStringPaths.Count > 0; } internal static Dictionary LoadEnvironmentStringPaths() @@ -44,15 +57,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search 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)) + if (splitSearch.Length == 2 && EnvStringPaths.ContainsKey(exactEnvStringPath)) { var queryPartToReplace = $"%{exactEnvStringPath}%"; - var expandedPath = envStringPaths[exactEnvStringPath]; + var expandedPath = EnvStringPaths[exactEnvStringPath]; // replace the %envstring% part of the query with its expanded equivalent return environmentVariablePath.Replace(queryPartToReplace, expandedPath); } @@ -64,7 +76,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search { var results = new List(); - var environmentVariables = LoadEnvironmentStringPaths(); var search = querySearch; if (querySearch.EndsWith("%") && search.Length > 1) @@ -72,9 +83,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search // 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)); @@ -91,7 +102,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search search = search.Substring(1); } - foreach (var p in environmentVariables) + foreach (var p in EnvStringPaths) { if (p.Key.StartsWith(search, StringComparison.InvariantCultureIgnoreCase)) { From 009ee3dc5c7a35daa36872f77a2f999cb8001815 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 12:34:36 +0800 Subject: [PATCH 04/26] Simplify environment variable detection --- .../Search/EnvironmentVariables.cs | 29 +++++++------------ .../Search/SearchManager.cs | 2 +- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index fae5f59cd..3d6a1ad8a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -23,12 +23,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal static bool IsEnvironmentVariableSearch(string search) { - return search.StartsWith("%") + return search.StartsWith("%") && search != "%%" - && !search.Contains("\\") + && !search.Contains('\\') && EnvStringPaths.Count > 0; } + internal static bool BeginsWithEnvironmentVar(string search) + { + return search[0] == '%' && search[1..].Contains("%\\"); + } + internal static Dictionary LoadEnvironmentStringPaths() { var envStringPaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); @@ -57,19 +62,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal static string TranslateEnvironmentVariablePath(string environmentVariablePath) { - 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; + return Environment.ExpandEnvironmentVariables(environmentVariablePath); } internal static List GetEnvironmentStringPathSuggestions(string querySearch, Query query, PluginInitContext context) @@ -86,9 +79,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (EnvStringPaths.ContainsKey(search)) { var expandedPath = EnvStringPaths[search]; - + results.Add(ResultManager.CreateFolderResult($"%{search}%", expandedPath, expandedPath, query)); - + return results; } } @@ -101,7 +94,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { search = search.Substring(1); } - + foreach (var p in EnvStringPaths) { if (p.Key.StartsWith(search, StringComparison.InvariantCultureIgnoreCase)) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 45ba690d3..677b7f038 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -175,7 +175,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return EnvironmentVariables.GetEnvironmentStringPathSuggestions(querySearch, query, Context); // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ - var isEnvironmentVariablePath = querySearch[0] == '%' && querySearch[1..].Contains("%\\"); + var isEnvironmentVariablePath = EnvironmentVariables.BeginsWithEnvironmentVar(querySearch); var locationPath = querySearch; From 52e72992c2d417e2fd5b3e149a568aa0fef6aabe Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 14:47:42 +0800 Subject: [PATCH 05/26] Change context and settings to non static --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 3 ++- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 8 ++++---- .../Search/WindowsIndex/WindowsIndexSearchManager.cs | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 36f0294a9..4075947ff 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -176,7 +176,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, @@ -193,6 +193,7 @@ namespace Flow.Launcher.Test.Plugins [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 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 677b7f038..ce1283e06 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) { @@ -144,7 +144,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search }; } - private static List EverythingContentSearchResult(Query query) + private List EverythingContentSearchResult(Query query) { return new List() { @@ -236,7 +236,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search 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) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs index abb50849d..f1060a4ac 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs @@ -97,10 +97,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex 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", @@ -108,7 +108,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"), 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); From 51f5d8a410e1708280e1c7e48c23951b4c8db37d Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 14:48:35 +0800 Subject: [PATCH 06/26] Add new constructor for EngineNotAvailableException --- .../Exceptions/EngineNotAvailableException.cs | 21 +++++++++++++++--- .../Everything/EverythingSearchManager.cs | 22 +++++++------------ .../WindowsIndex/WindowsIndexSearchManager.cs | 6 ++--- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Exceptions/EngineNotAvailableException.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Exceptions/EngineNotAvailableException.cs index 1a48892f5..a6315fb4b 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 class EngineNotAvailableException : Exception 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 class EngineNotAvailableException : Exception 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/Everything/EverythingSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/EverythingSearchManager.cs index ffd22d9f5..4bb8a0cdd 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 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything 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 @@ namespace Flow.Launcher.Plugin.Explorer.Search.Everything 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/WindowsIndex/WindowsIndexSearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs index f1060a4ac..c3a7d9e91 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/WindowsIndexSearchManager.cs @@ -106,6 +106,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex "Windows Index", api.GetTranslation("plugin_explorer_windowsSearchServiceFix"), api.GetTranslation("plugin_explorer_windowsSearchServiceNotRunning"), + Constants.WindowsIndexErrorImagePath, c => { Settings.WarnWindowsSearchServiceOff = false; @@ -114,10 +115,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex api.ChangeQuery(string.Empty); return ValueTask.FromResult(false); - }) - { - ErrorIcon = Constants.WindowsIndexErrorImagePath - }; + }); } } } From 383298a46bca314c26f1f6c1738c1935c35dcf19 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 16:04:41 +0800 Subject: [PATCH 07/26] Fix null subtitle when creating disk result --- Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs index 62ad71608..ed4f39735 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs @@ -71,7 +71,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { 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, From a6b7c5898b1a472b3d377f40e2b089b8d5c472b1 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 16:05:39 +0800 Subject: [PATCH 08/26] Fix subpath check --- .../Search/SearchManager.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index ce1283e06..4a25fe0ad 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Plugin.Explorer.Exceptions; +using System.IO; namespace Flow.Launcher.Plugin.Explorer.Search { @@ -119,7 +120,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any( - excludedPath => r.SubTitle.StartsWith(excludedPath.Path, StringComparison.OrdinalIgnoreCase))); + excludedPath => IsSubPathOf(r.SubTitle, excludedPath.Path))); return results.ToList(); } @@ -254,5 +255,16 @@ namespace Flow.Launcher.Plugin.Explorer.Search && search != "%%" && !search.Contains('\\'); } + + // https://stackoverflow.com/a/66877016 + internal static bool IsSubPathOf(string subPath, string basePath) + { + var rel = Path.GetRelativePath(basePath, subPath); + return rel != "." + && rel != ".." + && !rel.StartsWith("../") + && !rel.StartsWith(@"..\") + && !Path.IsPathRooted(rel); + } } } From 2c36692052f1f43782ee6f7f3fdf8416b7f7f6c7 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 16:06:01 +0800 Subject: [PATCH 09/26] Use case-insensitve comparator for path --- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 4a25fe0ad..da6509124 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -31,12 +31,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search 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() ?? ""); } } @@ -118,7 +119,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { throw new SearchException(engineName, e.Message, e); } - + results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any( excludedPath => IsSubPathOf(r.SubTitle, excludedPath.Path))); From 32268890b964e4e125ba81973541aa9588f05335 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 20:23:33 +0800 Subject: [PATCH 10/26] Remove TranslateEnvironmentVariablePath --- .../Search/EnvironmentVariables.cs | 5 ----- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index 3d6a1ad8a..c17277112 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -60,11 +60,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search return envStringPaths; } - internal static string TranslateEnvironmentVariablePath(string environmentVariablePath) - { - return Environment.ExpandEnvironmentVariables(environmentVariablePath); - } - internal static List GetEnvironmentStringPathSuggestions(string querySearch, Query query, PluginInitContext context) { var results = new List(); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index da6509124..25604cf2f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -178,11 +178,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ var isEnvironmentVariablePath = EnvironmentVariables.BeginsWithEnvironmentVar(querySearch); - + var locationPath = querySearch; if (isEnvironmentVariablePath) - locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath); + locationPath = Environment.ExpandEnvironmentVariables(locationPath); // Check that actual location exists, otherwise directory search will throw directory not found exception if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).LocationExists()) From ac92b93f669feb512a0ef0cb2ec1364f9b2b3377 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 20:29:25 +0800 Subject: [PATCH 11/26] Move IsSubPathOf to SharedCommands --- .../SharedCommands/FilesFolders.cs | 17 +++++++++++++++++ .../Search/SearchManager.cs | 13 +------------ .../Programs/Win32.cs | 15 ++------------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 5cb3a171a..97d8c25d3 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -241,5 +241,22 @@ namespace Flow.Launcher.Plugin.SharedCommands return path; } + + /// + /// Returns if is a sub path of . + /// From https://stackoverflow.com/a/66877016 + /// + /// + /// + /// + public static bool IsSubPathOf(string subPath, string basePath) + { + var rel = Path.GetRelativePath(basePath, subPath); + return rel != "." + && rel != ".." + && !rel.StartsWith("../") + && !rel.StartsWith(@"..\") + && !Path.IsPathRooted(rel); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 25604cf2f..324832990 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -121,7 +121,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any( - excludedPath => IsSubPathOf(r.SubTitle, excludedPath.Path))); + excludedPath => FilesFolders.IsSubPathOf(r.SubTitle, excludedPath.Path))); return results.ToList(); } @@ -256,16 +256,5 @@ namespace Flow.Launcher.Plugin.Explorer.Search && search != "%%" && !search.Contains('\\'); } - - // https://stackoverflow.com/a/66877016 - internal static bool IsSubPathOf(string subPath, string basePath) - { - var rel = Path.GetRelativePath(basePath, subPath); - return rel != "." - && rel != ".." - && !rel.StartsWith("../") - && !rel.StartsWith(@"..\") - && !Path.IsPathRooted(rel); - } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f678b6bc8..28eb9832f 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -471,7 +471,7 @@ namespace Flow.Launcher.Plugin.Program.Programs 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.IsSubPathOf(x, parent))) .AsParallel() .SelectMany(p => EnumerateProgramsInDir(p, suffixes, recursive: false)); @@ -763,17 +763,6 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - // 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,7 +774,7 @@ namespace Flow.Launcher.Plugin.Program.Programs HashSet parents = group.ToHashSet(); foreach (var source in group) { - if (parents.Any(p => IsSubPathOf(source.Location, p.Location) && + if (parents.Any(p => FilesFolders.IsSubPathOf(source.Location, p.Location) && source != p)) { parents.Remove(source); From beb144956c2d1c6163a5dd7f96486f5edd80c2a5 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 21:33:09 +0800 Subject: [PATCH 12/26] Rename method and add option to allow equality --- .../SharedCommands/FilesFolders.cs | 14 +++++++------- .../Search/SearchManager.cs | 2 +- .../Flow.Launcher.Plugin.Program/Programs/Win32.cs | 7 +++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 97d8c25d3..c59f05598 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.IO; -using System.Windows; namespace Flow.Launcher.Plugin.SharedCommands { @@ -243,16 +242,17 @@ namespace Flow.Launcher.Plugin.SharedCommands } /// - /// Returns if is a sub path of . + /// Returns if contains . /// From https://stackoverflow.com/a/66877016 /// - /// - /// + /// Parent path + /// Sub path + /// If , when and are equal, returns /// - public static bool IsSubPathOf(string subPath, string basePath) + public static bool PathContains(string parentPath, string subPath, bool allowEqual = false) { - var rel = Path.GetRelativePath(basePath, subPath); - return rel != "." + var rel = Path.GetRelativePath(parentPath, subPath); + return (rel != "." || allowEqual) && rel != ".." && !rel.StartsWith("../") && !rel.StartsWith(@"..\") diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 324832990..3a1dc3726 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -121,7 +121,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } results.RemoveWhere(r => Settings.IndexSearchExcludedSubdirectoryPaths.Any( - excludedPath => FilesFolders.IsSubPathOf(r.SubTitle, excludedPath.Path))); + excludedPath => FilesFolders.PathContains(excludedPath.Path, r.SubTitle))); return results.ToList(); } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 28eb9832f..e0079967b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -470,8 +470,8 @@ namespace Flow.Launcher.Plugin.Program.Programs } var paths = pathEnv.Split(";", StringSplitOptions.RemoveEmptyEntries).DistinctBy(p => p.ToLowerInvariant()); - - var toFilter = paths.Where(x => commonParents.All(parent => !FilesFolders.IsSubPathOf(x, parent))) + + var toFilter = paths.Where(x => commonParents.All(parent => !FilesFolders.PathContains(parent, x))) .AsParallel() .SelectMany(p => EnumerateProgramsInDir(p, suffixes, recursive: false)); @@ -774,8 +774,7 @@ namespace Flow.Launcher.Plugin.Program.Programs HashSet parents = group.ToHashSet(); foreach (var source in group) { - if (parents.Any(p => FilesFolders.IsSubPathOf(source.Location, p.Location) && - source != p)) + if (parents.Any(p => FilesFolders.PathContains(p.Location, source.Location))) { parents.Remove(source); } From b42fc54b806285067dd2c22cf600fd22c174c3b7 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 21:33:21 +0800 Subject: [PATCH 13/26] Add test for PathContains() --- Flow.Launcher.Test/FilesFoldersTest.cs | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Flow.Launcher.Test/FilesFoldersTest.cs diff --git a/Flow.Launcher.Test/FilesFoldersTest.cs b/Flow.Launcher.Test/FilesFoldersTest.cs new file mode 100644 index 000000000..656395591 --- /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 TestPathContains(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 TestPathContainsWhenEqual(string parentPath, string path, bool expectedResult) + { + Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, true)); + } + } +} From 4bea50d4cf9c738f03440e03d21f9801227b51ec Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:16:12 +0800 Subject: [PATCH 14/26] Add unit test for PathEqualityComparator --- Flow.Launcher.Test/FilesFoldersTest.cs | 2 +- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 41 +++++++++++++++++++ .../Search/SearchManager.cs | 14 ++++--- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Test/FilesFoldersTest.cs b/Flow.Launcher.Test/FilesFoldersTest.cs index 656395591..a65cc600d 100644 --- a/Flow.Launcher.Test/FilesFoldersTest.cs +++ b/Flow.Launcher.Test/FilesFoldersTest.cs @@ -44,7 +44,7 @@ namespace Flow.Launcher.Test [TestCase(@"c:\foo", @"c:\foo", true)] [TestCase(@"c:\foo\", @"c:\foo", true)] - [TestCase(@"c:\foo\", @"c:\foo", true)] + [TestCase(@"c:\foo", @"c:\foo\", true)] public void TestPathContainsWhenEqual(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 4075947ff..322441247 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -7,9 +7,11 @@ using Flow.Launcher.Plugin.SharedCommands; 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 { @@ -394,5 +396,44 @@ namespace Flow.Launcher.Test.Plugins // 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 PathEqualityComparatorEquality(string path1, string path2, bool expectedResult) + { + var comparator = PathEqualityComparator.Instance; + var result1 = new Result + { + Title = Path.GetFileName(path1), + SubTitle = path1 + }; + var result2 = new Result + { + Title = Path.GetFileName(path2), + SubTitle = path2 + }; + Assert.AreEqual(expectedResult, comparator.Equals(result1, result2)); + } + + [TestCase(@"c:\foo\", @"c:\foo\")] + [TestCase(@"C:\Foo\", @"c:\foo\")] + public void PathEqualityComparatorHashCode(string path1, string path2) + { + 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); + Assert.IsTrue(hash1 == hash2); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 3a1dc3726..0a40590ba 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Plugin.Explorer.Exceptions; -using System.IO; namespace Flow.Launcher.Plugin.Explorer.Search { @@ -24,14 +23,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search Settings = settings; } - private class PathEqualityComparator : IEqualityComparer + /// + /// Note: Assuming all diretories end with "\". + /// + 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.Equals(y.Title, StringComparison.OrdinalIgnoreCase) + return x.Title.Equals(y.Title, StringComparison.OrdinalIgnoreCase) && string.Equals(x.SubTitle, y.SubTitle, StringComparison.OrdinalIgnoreCase); } @@ -178,7 +180,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ var isEnvironmentVariablePath = EnvironmentVariables.BeginsWithEnvironmentVar(querySearch); - + var locationPath = querySearch; if (isEnvironmentVariablePath) @@ -249,10 +251,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search 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('\\'); } From 03f062ca34c49e6cb6223f867718436116969ddd Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:26:10 +0800 Subject: [PATCH 15/26] Fix build error --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index c59f05598..2ccc35884 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -1,6 +1,9 @@ using System; using System.Diagnostics; using System.IO; +#if !DEBUG +using System.Windows; +#endif namespace Flow.Launcher.Plugin.SharedCommands { From fefb1378704291ae6e70ee8691640718333ac2f0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 20 Jan 2023 07:45:39 +1100 Subject: [PATCH 16/26] fix test format --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 322441247..70c13f296 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -400,8 +400,9 @@ namespace Flow.Launcher.Test.Plugins [TestCase(@"c:\foo", @"c:\foo", true)] [TestCase(@"C:\Foo\", @"c:\foo\", true)] [TestCase(@"c:\foo", @"c:\foo\", false)] - public void PathEqualityComparatorEquality(string path1, string path2, bool expectedResult) + public void GivenTwoPaths_WhenCompared_ThenShouldBeExpectedSameOrDifferent(string path1, string path2, bool expectedResult) { + // Given var comparator = PathEqualityComparator.Instance; var result1 = new Result { @@ -413,13 +414,16 @@ namespace Flow.Launcher.Test.Plugins 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 PathEqualityComparatorHashCode(string path1, string path2) + public void GivenTwoPaths_WhenComparedHasCode_ThenShouldBeSame(string path1, string path2) { + // Given var comparator = PathEqualityComparator.Instance; var result1 = new Result { @@ -431,8 +435,11 @@ namespace Flow.Launcher.Test.Plugins Title = Path.GetFileName(path2), SubTitle = path2 }; + var hash1 = comparator.GetHashCode(result1); var hash2 = comparator.GetHashCode(result2); + + // When, Then Assert.IsTrue(hash1 == hash2); } } From b77bfea8875be532cafacfcefce6a14c34023ea6 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 20 Jan 2023 07:55:16 +1100 Subject: [PATCH 17/26] update test name --- Flow.Launcher.Test/FilesFoldersTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Test/FilesFoldersTest.cs b/Flow.Launcher.Test/FilesFoldersTest.cs index a65cc600d..d16826053 100644 --- a/Flow.Launcher.Test/FilesFoldersTest.cs +++ b/Flow.Launcher.Test/FilesFoldersTest.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.SharedCommands; +using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; namespace Flow.Launcher.Test @@ -37,7 +37,7 @@ namespace Flow.Launcher.Test [TestCase(@"c:\foo", @"c:\foo", false)] [TestCase(@"c:\foo\", @"c:\foo", false)] [TestCase(@"c:\foo", @"c:\foo\", false)] - public void TestPathContains(string parentPath, string path, bool expectedResult) + public void GivenTwoPaths_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult) { Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path)); } @@ -45,7 +45,7 @@ namespace Flow.Launcher.Test [TestCase(@"c:\foo", @"c:\foo", true)] [TestCase(@"c:\foo\", @"c:\foo", true)] [TestCase(@"c:\foo", @"c:\foo\", true)] - public void TestPathContainsWhenEqual(string parentPath, string path, bool expectedResult) + public void GivenTwoPathsAreTheSame_WhenCheckPathContains_ThenShouldBeTrue(string parentPath, string path, bool expectedResult) { Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, true)); } From 768ed4f80a2b7f83530ff7698cdc0fb30a87fe59 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 20 Jan 2023 12:13:02 +0800 Subject: [PATCH 18/26] Update wording Co-authored-by: Jeremy Wu --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 0a40590ba..916399e5c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -24,7 +24,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } /// - /// Note: Assuming all diretories end with "\". + /// Note: A path that ends with "\" and one that doesn't will not be regarded as equal. /// public class PathEqualityComparator : IEqualityComparer { From 504fb4ae0588735635955ac5a580e01baafc6175 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 20 Jan 2023 14:02:04 +0800 Subject: [PATCH 19/26] Display environment vars in upper case --- .../Search/EnvironmentVariables.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index c17277112..130de2ab1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -52,8 +52,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search path = Path.IsPathFullyQualified(path) ? path : Path.GetFullPath(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); } } From 4d267fe71ee0f3aeb88f8781cb9aa4f5739cb49d Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 20 Jan 2023 14:27:12 +0800 Subject: [PATCH 20/26] Fix incorrect %homepath% parsing Parse environment variables before checking path existence --- .../SharedCommands/FilesFolders.cs | 10 ++++++++++ .../Search/EnvironmentVariables.cs | 20 ++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 2ccc35884..42a0e0c73 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -261,5 +261,15 @@ namespace Flow.Launcher.Plugin.SharedCommands && !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/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index 130de2ab1..a649de58e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; -using System.Text; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.Explorer.Search { @@ -37,20 +37,22 @@ namespace Flow.Launcher.Plugin.Explorer.Search internal static Dictionary LoadEnvironmentStringPaths() { var envStringPaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + var homedrive = Environment.GetEnvironmentVariable("HOMEDRIVE")?.EnsureTrailingSlash() ?? "C:\\"; foreach (DictionaryEntry special in Environment.GetEnvironmentVariables()) { var path = special.Value.ToString(); + // 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 + // Add basepath for GetFullPath() to parse %HOMEPATH% correctly + path = Path.IsPathFullyQualified(path) ? path : Path.GetFullPath(path, homedrive); + 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; - - // 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); - // Variables are returned with a mixture of all upper/lower case. // Call ToUpper() to make the results look consistent envStringPaths.Add(special.Key.ToString().ToUpper(), path); From 966d3e752efc3f7e52a83bfb3d8e46b62478c6fc Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:03:40 +0800 Subject: [PATCH 21/26] Fix environment variable expansion logic --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 17 +++++++++++++++++ .../Search/EnvironmentVariables.cs | 10 ++++++++-- .../Search/SearchManager.cs | 14 ++++---------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 70c13f296..e9d37433f 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -442,5 +442,22 @@ namespace Flow.Launcher.Test.Plugins // 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/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index a649de58e..5b0a5a8bd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.Explorer.Search @@ -29,9 +30,14 @@ namespace Flow.Launcher.Plugin.Explorer.Search && EnvStringPaths.Count > 0; } - internal static bool BeginsWithEnvironmentVar(string search) + public static bool HasEnvironmentVar(string search) { - return search[0] == '%' && search[1..].Contains("%\\"); + // "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); } internal static Dictionary LoadEnvironmentStringPaths() diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 916399e5c..83057ff04 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -173,18 +173,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search 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 = EnvironmentVariables.BeginsWithEnvironmentVar(querySearch); - - var locationPath = querySearch; - - if (isEnvironmentVariablePath) - locationPath = Environment.ExpandEnvironmentVariables(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()) From 71a2f996dbff2c92b219aa5641af01ef50870622 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:27:19 +0800 Subject: [PATCH 22/26] Set environment var dict in LoadEnvironmentStringPaths() --- .../Search/EnvironmentVariables.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs index 5b0a5a8bd..e526fb85a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/EnvironmentVariables.cs @@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { if (_envStringPaths == null) { - _envStringPaths = LoadEnvironmentStringPaths(); + LoadEnvironmentStringPaths(); } return _envStringPaths; } @@ -40,9 +40,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search dir.Split('%').Length == 3); } - internal static Dictionary LoadEnvironmentStringPaths() + private static void LoadEnvironmentStringPaths() { - var envStringPaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + _envStringPaths = new Dictionary(StringComparer.InvariantCultureIgnoreCase); var homedrive = Environment.GetEnvironmentVariable("HOMEDRIVE")?.EnsureTrailingSlash() ?? "C:\\"; foreach (DictionaryEntry special in Environment.GetEnvironmentVariables()) @@ -61,11 +61,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search { // Variables are returned with a mixture of all upper/lower case. // Call ToUpper() to make the results look consistent - envStringPaths.Add(special.Key.ToString().ToUpper(), path); + _envStringPaths.Add(special.Key.ToString().ToUpper(), path); } } - - return envStringPaths; } internal static List GetEnvironmentStringPathSuggestions(string querySearch, Query query, PluginInitContext context) From b739bb49d307e3d1d058bd3504e43139e260bbf3 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:36:14 +0800 Subject: [PATCH 23/26] Fix CI test failure --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 42a0e0c73..a6cf0755c 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -254,7 +254,7 @@ namespace Flow.Launcher.Plugin.SharedCommands /// public static bool PathContains(string parentPath, string subPath, bool allowEqual = false) { - var rel = Path.GetRelativePath(parentPath, subPath); + var rel = Path.GetRelativePath(parentPath.EnsureTrailingSlash(), subPath); return (rel != "." || allowEqual) && rel != ".." && !rel.StartsWith("../") From f5cc792ce9518f50dad54882a5fdd579a13ccd5c Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 21 Jan 2023 16:57:41 +0800 Subject: [PATCH 24/26] Return empty when operation canceled --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 83057ff04..51c4c3d9d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -111,7 +111,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } catch (OperationCanceledException) { - return results.ToList(); + return new List(); } catch (EngineNotAvailableException) { From babffe907f2a320b6e05bfe798c32d07d96c71d2 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 22 Jan 2023 12:59:32 +0800 Subject: [PATCH 25/26] Disable unused import warning --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index a6cf0755c..cfcaaa44d 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -1,9 +1,9 @@ using System; using System.Diagnostics; using System.IO; -#if !DEBUG +#pragma warning disable IDE0005 using System.Windows; -#endif +#pragma warning restore IDE0005 namespace Flow.Launcher.Plugin.SharedCommands { From dd02ad37bc2a620b3493614b649cbd386e7a3276 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 22 Jan 2023 13:21:53 +0800 Subject: [PATCH 26/26] Refactor --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index cfcaaa44d..bd8d32ff5 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -208,22 +208,16 @@ namespace Flow.Launcher.Plugin.SharedCommands /// 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; } ///