From 15a5bbabb8f8dbbcbde05c745c32c50ae34289c1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 10 Jan 2021 13:17:40 +1100 Subject: [PATCH 01/88] version bump for PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index ef2c1255a..60ab7ab18 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.4.1", + "Version": "1.5.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From 97052b1fbf3bb826a2dac4c4dcee92879161d32f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 10 Jan 2021 11:13:46 +0800 Subject: [PATCH 02/88] fix merge issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 160 ++++++++++++----------- 1 file changed, 83 insertions(+), 77 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3751d8c61..ac63f0cc3 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -54,7 +54,6 @@ namespace Flow.Launcher.ViewModel private BufferBlock _resultsUpdateQueue; private Task _resultsViewUpdateTask; - #endregion #region Constructor @@ -95,7 +94,7 @@ namespace Flow.Launcher.ViewModel { foreach (var pair in PluginManager.GetPluginsForInterface()) { - var plugin = (IResultUpdated)pair.Plugin; + var plugin = (IResultUpdated) pair.Plugin; plugin.ResultsUpdated += (s, e) => { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); @@ -108,7 +107,8 @@ namespace Flow.Launcher.ViewModel private void RegisterViewUpdate() { _resultsUpdateQueue = new BufferBlock(); - _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); + _resultsViewUpdateTask = + Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); async Task updateAction() @@ -119,7 +119,9 @@ namespace Flow.Launcher.ViewModel _resultsUpdateQueue.TryReceiveAll(out var queue); UpdateResultView(queue.Where(r => !r.Token.IsCancellationRequested)); } - }; + } + + ; void continueAction(Task t) { @@ -127,7 +129,8 @@ namespace Flow.Launcher.ViewModel throw t.Exception; #else Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}"); - _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); + _resultsViewUpdateTask = + Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); #endif } } @@ -147,25 +150,13 @@ namespace Flow.Launcher.ViewModel } }); - SelectNextItemCommand = new RelayCommand(_ => - { - SelectedResults.SelectNextResult(); - }); + SelectNextItemCommand = new RelayCommand(_ => { SelectedResults.SelectNextResult(); }); - SelectPrevItemCommand = new RelayCommand(_ => - { - SelectedResults.SelectPrevResult(); - }); + SelectPrevItemCommand = new RelayCommand(_ => { SelectedResults.SelectPrevResult(); }); - SelectNextPageCommand = new RelayCommand(_ => - { - SelectedResults.SelectNextPage(); - }); + SelectNextPageCommand = new RelayCommand(_ => { SelectedResults.SelectNextPage(); }); - SelectPrevPageCommand = new RelayCommand(_ => - { - SelectedResults.SelectPrevPage(); - }); + SelectPrevPageCommand = new RelayCommand(_ => { SelectedResults.SelectPrevPage(); }); SelectFirstResultCommand = new RelayCommand(_ => SelectedResults.SelectFirstResult()); @@ -244,6 +235,7 @@ namespace Flow.Launcher.ViewModel private string _lastQueryText; private string _queryText; + public string QueryText { get { return _queryText; } @@ -264,10 +256,12 @@ namespace Flow.Launcher.ViewModel QueryTextCursorMovedToEnd = true; QueryText = queryText; } + public bool LastQuerySelected { get; set; } public bool QueryTextCursorMovedToEnd { get; set; } private ResultsViewModel _selectedResults; + private ResultsViewModel SelectedResults { get { return _selectedResults; } @@ -299,6 +293,7 @@ namespace Flow.Launcher.ViewModel QueryText = string.Empty; } } + _selectedResults.Visbility = Visibility.Visible; } } @@ -359,7 +354,7 @@ namespace Flow.Launcher.ViewModel var filtered = results.Where ( r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet() - || StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet() + || StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet() ).ToList(); ContextMenu.AddResults(filtered, id); } @@ -386,7 +381,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query { RawQuery = h.Query }, + OriginQuery = new Query {RawQuery = h.Query}, Action = _ => { SelectedResults = Results; @@ -436,64 +431,73 @@ namespace Flow.Launcher.ViewModel var plugins = PluginManager.ValidPluginsForQuery(query); Task.Run(async () => - { - if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) { - // Wait 45 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(45, currentCancellationToken); + if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) + { + // Wait 45 millisecond for query change in global query + // if query changes, return so that it won't be calculated + await Task.Delay(45, currentCancellationToken); + if (currentCancellationToken.IsCancellationRequested) + return; + } + + _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => + { + // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet + if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) + { + ProgressBarVisibility = Visibility.Visible; + } + }, currentCancellationToken); + + // so looping will stop once it was cancelled + var parallelOptions = new ParallelOptions {CancellationToken = currentCancellationToken}; + try + { + Parallel.ForEach(plugins, parallelOptions, plugin => + { + if (!plugin.Metadata.Disabled) + { + try + { + var results = PluginManager.QueryForPlugin(plugin, query); + _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, + query, + currentCancellationToken)); + } + catch (Exception e) + { + Log.Exception("MainViewModel", + $"Exception when querying the plugin {plugin.Metadata.Name}", e, + "QueryResults"); + } + } + }); + } + catch (OperationCanceledException) + { + // nothing to do here + } + if (currentCancellationToken.IsCancellationRequested) return; - } - _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => - { - // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet - if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) + // this should happen once after all queries are done so progress bar should continue + // until the end of all querying + _isQueryRunning = false; + if (!currentCancellationToken.IsCancellationRequested) { - ProgressBarVisibility = Visibility.Visible; + // update to hidden if this is still the current query + ProgressBarVisibility = Visibility.Hidden; } - }, currentCancellationToken); - - // so looping will stop once it was cancelled - var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken }; - try - { - Parallel.ForEach(plugins, parallelOptions, plugin => + }, currentCancellationToken) + .ContinueWith( + t => { - if (!plugin.Metadata.Disabled) - { - try - { - var results = PluginManager.QueryForPlugin(plugin, query); - UpdateResultView(results, plugin.Metadata, query); - } - catch (Exception e) - { - Log.Exception("MainViewModel", $"Exception when querying the plugin {plugin.Metadata.Name}", e, "QueryResults"); - } - } - }); - } - catch (OperationCanceledException) - { - // nothing to do here - } - - if (currentCancellationToken.IsCancellationRequested) - return; - - // this should happen once after all queries are done so progress bar should continue - // until the end of all querying - _isQueryRunning = false; - if (!currentCancellationToken.IsCancellationRequested) - { // update to hidden if this is still the current query - ProgressBarVisibility = Visibility.Hidden; - } - }, currentCancellationToken).ContinueWith(t => - { - Log.Exception("MainViewModel", "Error when querying plugins", t.Exception?.InnerException, "QueryResults"); - }, TaskContinuationOptions.OnlyOnFaulted); + Log.Exception("MainViewModel", "Error when querying plugins", + t.Exception?.InnerException, + "QueryResults"); + }, TaskContinuationOptions.OnlyOnFaulted); } } else @@ -561,6 +565,7 @@ namespace Flow.Launcher.ViewModel } }; } + return menu; } @@ -606,6 +611,7 @@ namespace Flow.Launcher.ViewModel var selected = SelectedResults == History; return selected; } + #region Hotkey private void SetHotkey(string hotkeyStr, EventHandler action) @@ -624,7 +630,8 @@ namespace Flow.Launcher.ViewModel catch (Exception) { string errorMsg = - string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), + hotkeyStr); MessageBox.Show(errorMsg); } } @@ -674,7 +681,6 @@ namespace Flow.Launcher.ViewModel { if (!ShouldIgnoreHotkeys()) { - if (_settings.LastQueryMode == LastQueryMode.Empty) { ChangeQueryText(string.Empty); @@ -724,6 +730,7 @@ namespace Flow.Launcher.ViewModel _saved = true; } } + /// /// To avoid deadlock, this method should not called from main thread /// @@ -787,7 +794,6 @@ namespace Flow.Launcher.ViewModel { Results.AddResults(list, metadata.ID); } - } #endregion From 691c2b06f9f3e156d851cecef3d351237a794234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 10 Jan 2021 11:14:00 +0800 Subject: [PATCH 03/88] increase edittime boundary --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index fc77327cc..932d6fc4b 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -336,7 +336,7 @@ namespace Flow.Launcher.ViewModel if (Count == 0 && newItems.Count == 0 || _token.IsCancellationRequested) return; - if (editTime < 5 || newItems.Count < 30) + if (editTime < 10 || newItems.Count < 30) { if (Count != 0) ClearItems(); AddRange(newItems); From 3892b59724d7db36adb2725b05abbc2ccfd12677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 13 Jan 2021 09:25:46 +0800 Subject: [PATCH 04/88] Remove extra null check (use string.iswhitespaceornull) --- Flow.Launcher/ViewModel/MainViewModel.cs | 187 +++++++++++------------ 1 file changed, 92 insertions(+), 95 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ac63f0cc3..0152cd854 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -94,7 +94,7 @@ namespace Flow.Launcher.ViewModel { foreach (var pair in PluginManager.GetPluginsForInterface()) { - var plugin = (IResultUpdated) pair.Plugin; + var plugin = (IResultUpdated)pair.Plugin; plugin.ResultsUpdated += (s, e) => { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); @@ -381,7 +381,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query {RawQuery = h.Query}, + OriginQuery = new Query { RawQuery = h.Query }, Action = _ => { SelectedResults = Results; @@ -409,105 +409,102 @@ namespace Flow.Launcher.ViewModel private void QueryResults() { - if (!string.IsNullOrEmpty(QueryText)) + _updateSource?.Cancel(); + + if (string.IsNullOrWhiteSpace(QueryText)) { - _updateSource?.Cancel(); - _updateSource?.Dispose(); - - var currentUpdateSource = new CancellationTokenSource(); - _updateSource = currentUpdateSource; - var currentCancellationToken = _updateSource.Token; - _updateToken = currentCancellationToken; - - ProgressBarVisibility = Visibility.Hidden; - _isQueryRunning = true; - var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins); - if (query != null) - { - // handle the exclusiveness of plugin using action keyword - RemoveOldQueryResults(query); - - _lastQuery = query; - - var plugins = PluginManager.ValidPluginsForQuery(query); - Task.Run(async () => - { - if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) - { - // Wait 45 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(45, currentCancellationToken); - if (currentCancellationToken.IsCancellationRequested) - return; - } - - _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => - { - // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet - if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) - { - ProgressBarVisibility = Visibility.Visible; - } - }, currentCancellationToken); - - // so looping will stop once it was cancelled - var parallelOptions = new ParallelOptions {CancellationToken = currentCancellationToken}; - try - { - Parallel.ForEach(plugins, parallelOptions, plugin => - { - if (!plugin.Metadata.Disabled) - { - try - { - var results = PluginManager.QueryForPlugin(plugin, query); - _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, - query, - currentCancellationToken)); - } - catch (Exception e) - { - Log.Exception("MainViewModel", - $"Exception when querying the plugin {plugin.Metadata.Name}", e, - "QueryResults"); - } - } - }); - } - catch (OperationCanceledException) - { - // nothing to do here - } - - if (currentCancellationToken.IsCancellationRequested) - return; - - // this should happen once after all queries are done so progress bar should continue - // until the end of all querying - _isQueryRunning = false; - if (!currentCancellationToken.IsCancellationRequested) - { - // update to hidden if this is still the current query - ProgressBarVisibility = Visibility.Hidden; - } - }, currentCancellationToken) - .ContinueWith( - t => - { - Log.Exception("MainViewModel", "Error when querying plugins", - t.Exception?.InnerException, - "QueryResults"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - } - else - { - _updateSource?.Cancel(); Results.Clear(); Results.Visbility = Visibility.Collapsed; + return; } + + _updateSource?.Dispose(); + + var currentUpdateSource = new CancellationTokenSource(); + _updateSource = currentUpdateSource; + var currentCancellationToken = _updateSource.Token; + _updateToken = currentCancellationToken; + + ProgressBarVisibility = Visibility.Hidden; + _isQueryRunning = true; + + var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins); + + // handle the exclusiveness of plugin using action keyword + RemoveOldQueryResults(query); + + _lastQuery = query; + + var plugins = PluginManager.ValidPluginsForQuery(query); + + Task.Run(async () => + { + if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) + { + // Wait 45 millisecond for query change in global query + // if query changes, return so that it won't be calculated + await Task.Delay(45, currentCancellationToken); + if (currentCancellationToken.IsCancellationRequested) + return; + } + + _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => + { + // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet + if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) + { + ProgressBarVisibility = Visibility.Visible; + } + }, currentCancellationToken); + + // so looping will stop once it was cancelled + var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken }; + try + { + Parallel.ForEach(plugins, parallelOptions, plugin => + { + if (!plugin.Metadata.Disabled) + { + try + { + var results = PluginManager.QueryForPlugin(plugin, query); + _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, + query, + currentCancellationToken)); + } + catch (Exception e) + { + Log.Exception("MainViewModel", + $"Exception when querying the plugin {plugin.Metadata.Name}", e, + "QueryResults"); + } + } + }); + } + catch (OperationCanceledException) + { + return; + // nothing to do here + } + + if (currentCancellationToken.IsCancellationRequested) + return; + + // this should happen once after all queries are done so progress bar should continue + // until the end of all querying + _isQueryRunning = false; + if (!currentCancellationToken.IsCancellationRequested) + { + // update to hidden if this is still the current query + ProgressBarVisibility = Visibility.Hidden; + } + }, currentCancellationToken) + .ContinueWith(t => Log.Exception("MainViewModel", "Error when querying plugins", t.Exception?.InnerException, "QueryResults") + , TaskContinuationOptions.OnlyOnFaulted); + } + private void RemoveOldQueryResults(Query query) { string lastKeyword = _lastQuery.ActionKeyword; From c39dfab6e55569a5538b1e37401a26bfc9bd7008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 13 Jan 2021 09:43:15 +0800 Subject: [PATCH 05/88] fix priority scoring --- Flow.Launcher/ViewModel/MainViewModel.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 0d79a55ec..cdac8f90d 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -755,15 +755,19 @@ namespace Flow.Launcher.ViewModel #endif - foreach (var result in resultsForUpdates.SelectMany(u => u.Results)) + foreach (var metaResults in resultsForUpdates) { - if (_topMostRecord.IsTopMost(result)) + foreach (var result in metaResults.Results) { - result.Score = int.MaxValue; - } - else - { - result.Score += _userSelectedRecord.GetSelectedCount(result) * 5; + if (_topMostRecord.IsTopMost(result)) + { + result.Score = int.MaxValue; + } + else + { + var priorityScore = metaResults.Metadata.Priority * 50; + result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore; + } } } From 763b51858fc094eb158b98ef023f9ccb24d8d44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 14 Jan 2021 12:24:41 +0800 Subject: [PATCH 06/88] Add comment in IPublic, IAsyncPlugin, IReloadable, IAsyncReloadable --- Flow.Launcher.Plugin/IAsyncPlugin.cs | 15 +++++++++++++ Flow.Launcher.Plugin/IPlugin.cs | 22 ++++++++++++++++++- .../Interfaces/IAsyncReloadable.cs | 11 ++++++++++ .../Interfaces/IReloadable.cs | 6 ++++- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index 36f098e7d..3e1a086bc 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -4,9 +4,24 @@ using System.Threading.Tasks; namespace Flow.Launcher.Plugin { + /// + /// Asynchronous Plugin Model for Flow Launcher + /// public interface IAsyncPlugin { + /// + /// Asynchronous Querying + /// + /// Query to search + /// Cancel when querying job is obsolete + /// Task> QueryAsync(Query query, CancellationToken token); + + /// + /// Initialize plugin asynchrously (will still wait finish to continue) + /// + /// + /// Task InitAsync(PluginInitContext context); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 4cc6d8d40..41c61a608 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -2,10 +2,30 @@ namespace Flow.Launcher.Plugin { + /// + /// Synchronous Plugin Model for Flow Launcher + /// + /// If you assume that Querying or Init method require IO transmission + /// or CPU Intense Job (performing better with cancellation), please try IAsyncPlugin interface + /// + /// public interface IPlugin { + /// + /// Querying when user's search changes + /// + /// This method will be called within a Task.Run, + /// so please avoid synchrously wait for long. + /// + /// + /// Query to search + /// List Query(Query query); - + + /// + /// Initialize plugin + /// + /// void Init(PluginInitContext context); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs index 9c922f667..434d83646 100644 --- a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs @@ -2,6 +2,17 @@ namespace Flow.Launcher.Plugin { + /// + /// This interface is to indicate and allow plugins to asyncronously reload their + /// in memory data cache or other mediums when user makes a new change + /// that is not immediately captured. For example, for BrowserBookmark and Program + /// plugin does not automatically detect when a user added a new bookmark or program, + /// so this interface's function is exposed to allow user manually do the reloading after + /// those new additions. + /// + /// The command that allows user to manual reload is exposed via Plugin.Sys, and + /// it will call the plugins that have implemented this interface. + /// public interface IAsyncReloadable { Task ReloadDataAsync(); diff --git a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs index 29b3c15c9..d9160b0ea 100644 --- a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs @@ -1,7 +1,7 @@ namespace Flow.Launcher.Plugin { /// - /// This interface is to indicate and allow plugins to reload their + /// This interface is to indicate and allow plugins to synchronously reload their /// in memory data cache or other mediums when user makes a new change /// that is not immediately captured. For example, for BrowserBookmark and Program /// plugin does not automatically detect when a user added a new bookmark or program, @@ -10,6 +10,10 @@ /// /// The command that allows user to manual reload is exposed via Plugin.Sys, and /// it will call the plugins that have implemented this interface. + /// + /// + /// If requiring reloading data asynchrouly, please try IAsyncReloadable + /// /// public interface IReloadable { From 3d69998bc3fa2e66b53c1fae04a3d865daedcc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Thu, 14 Jan 2021 19:54:37 +0800 Subject: [PATCH 07/88] fix potential duplicate item due to IResultUpdate event --- Flow.Launcher/ViewModel/MainViewModel.cs | 131 ++++++++++++----------- 1 file changed, 69 insertions(+), 62 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index cdac8f90d..233611be0 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -94,7 +94,7 @@ namespace Flow.Launcher.ViewModel { foreach (var pair in PluginManager.GetPluginsForInterface()) { - var plugin = (IResultUpdated)pair.Plugin; + var plugin = (IResultUpdated) pair.Plugin; plugin.ResultsUpdated += (s, e) => { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); @@ -113,11 +113,18 @@ namespace Flow.Launcher.ViewModel async Task updateAction() { + var queue = new Dictionary(); while (await _resultsUpdateQueue.OutputAvailableAsync()) { + queue.Clear(); await Task.Delay(20); - _resultsUpdateQueue.TryReceiveAll(out var queue); - UpdateResultView(queue.Where(r => !r.Token.IsCancellationRequested)); + while (_resultsUpdateQueue.TryReceive(out var item)) + { + if (!item.Token.IsCancellationRequested) + queue[item.ID] = item; + } + + UpdateResultView(queue.Values); } } @@ -381,7 +388,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query { RawQuery = h.Query }, + OriginQuery = new Query {RawQuery = h.Query}, Action = _ => { SelectedResults = Results; @@ -438,70 +445,70 @@ namespace Flow.Launcher.ViewModel var plugins = PluginManager.ValidPluginsForQuery(query); Task.Run(async () => - { - if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) { - // Wait 45 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(45, currentCancellationToken); + if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) + { + // Wait 45 millisecond for query change in global query + // if query changes, return so that it won't be calculated + await Task.Delay(45, currentCancellationToken); + if (currentCancellationToken.IsCancellationRequested) + return; + } + + _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => + { + // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet + if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) + { + ProgressBarVisibility = Visibility.Visible; + } + }, currentCancellationToken); + + // so looping will stop once it was cancelled + var parallelOptions = new ParallelOptions {CancellationToken = currentCancellationToken}; + try + { + Parallel.ForEach(plugins, parallelOptions, plugin => + { + if (!plugin.Metadata.Disabled) + { + try + { + var results = PluginManager.QueryForPlugin(plugin, query); + _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, + query, + currentCancellationToken)); + } + catch (Exception e) + { + Log.Exception("MainViewModel", + $"Exception when querying the plugin {plugin.Metadata.Name}", e, + "QueryResults"); + } + } + }); + } + catch (OperationCanceledException) + { + return; + // nothing to do here + } + if (currentCancellationToken.IsCancellationRequested) return; - } - _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => - { - // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet - if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) + // this should happen once after all queries are done so progress bar should continue + // until the end of all querying + _isQueryRunning = false; + if (!currentCancellationToken.IsCancellationRequested) { - ProgressBarVisibility = Visibility.Visible; + // update to hidden if this is still the current query + ProgressBarVisibility = Visibility.Hidden; } - }, currentCancellationToken); - - // so looping will stop once it was cancelled - var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken }; - try - { - Parallel.ForEach(plugins, parallelOptions, plugin => - { - if (!plugin.Metadata.Disabled) - { - try - { - var results = PluginManager.QueryForPlugin(plugin, query); - _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, - query, - currentCancellationToken)); - } - catch (Exception e) - { - Log.Exception("MainViewModel", - $"Exception when querying the plugin {plugin.Metadata.Name}", e, - "QueryResults"); - } - } - }); - } - catch (OperationCanceledException) - { - return; - // nothing to do here - } - - if (currentCancellationToken.IsCancellationRequested) - return; - - // this should happen once after all queries are done so progress bar should continue - // until the end of all querying - _isQueryRunning = false; - if (!currentCancellationToken.IsCancellationRequested) - { - // update to hidden if this is still the current query - ProgressBarVisibility = Visibility.Hidden; - } - }, currentCancellationToken) - .ContinueWith(t => Log.Exception("MainViewModel", "Error when querying plugins", t.Exception?.InnerException, "QueryResults") - , TaskContinuationOptions.OnlyOnFaulted); - + }, currentCancellationToken) + .ContinueWith(t => Log.Exception("MainViewModel", "Error when querying plugins", + t.Exception?.InnerException, "QueryResults") + , TaskContinuationOptions.OnlyOnFaulted); } From 723265a865afa75f7f250ab9f93668298e093d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 15 Jan 2021 10:37:13 +0800 Subject: [PATCH 08/88] fix TopMostRecord and UserSelectedRecord due to System.Text.Json cannot parse private field in version 3.1 --- .../Storage/JsonStorage.cs | 4 +-- Flow.Launcher/Storage/TopMostRecord.cs | 26 ++++++++++++++----- Flow.Launcher/Storage/UserSelectedRecord.cs | 16 +++++++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 268dc20b8..f0e4a79fc 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -9,7 +9,7 @@ namespace Flow.Launcher.Infrastructure.Storage /// /// Serialize object using json format. /// - public class JsonStrorage + public class JsonStrorage where T : new() { private readonly JsonSerializerOptions _serializerSettings; private T _data; @@ -76,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Storage BackupOriginFile(); } - _data = JsonSerializer.Deserialize("{}", _serializerSettings); + _data = new T(); Save(); } diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index 09e69f6d8..c92ef4956 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Text.Json.Serialization; using Flow.Launcher.Plugin; namespace Flow.Launcher.Storage @@ -8,20 +9,24 @@ namespace Flow.Launcher.Storage // todo this class is not thread safe.... but used from multiple threads. public class TopMostRecord { - private Dictionary records = new Dictionary(); + /// + /// You should not directly access this field + /// + /// It is public due to System.Text.Json limitation in version 3.1 + /// + /// + /// TODO: Set it to private + public Dictionary records { get; set; } = new Dictionary(); internal bool IsTopMost(Result result) { - if (records.Count == 0) + if (records.Count == 0 || !records.ContainsKey(result.OriginQuery.RawQuery)) { return false; } - // since this dictionary should be very small (or empty) going over it should be pretty fast. - return records.Any(o => o.Value.Title == result.Title - && o.Value.SubTitle == result.SubTitle - && o.Value.PluginID == result.PluginID - && o.Key == result.OriginQuery.RawQuery); + // since this dictionary should be very small (or empty) going over it should be pretty fast. + return records[result.OriginQuery.RawQuery].Equals(result); } internal void Remove(Result result) @@ -53,5 +58,12 @@ namespace Flow.Launcher.Storage public string Title { get; set; } public string SubTitle { get; set; } public string PluginID { get; set; } + + public bool Equals(Result r) + { + return Title == r.Title + && SubTitle == r.SubTitle + && PluginID == r.PluginID; + } } } diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs index c7ffe1a1d..b97138eb9 100644 --- a/Flow.Launcher/Storage/UserSelectedRecord.cs +++ b/Flow.Launcher/Storage/UserSelectedRecord.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; @@ -6,7 +7,20 @@ namespace Flow.Launcher.Storage { public class UserSelectedRecord { - private Dictionary records = new Dictionary(); + /// + /// You should not directly access this field + /// + /// It is public due to System.Text.Json limitation in version 3.1 + /// + /// + /// TODO: Set it to private + [JsonPropertyName("records")] + public Dictionary records { get; set; } + + public UserSelectedRecord() + { + records = new Dictionary(); + } public void Add(Result result) { From 6655a8f168c9d1ae0586b8d52f3ce91fd3f9e321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 15 Jan 2021 15:56:28 +0800 Subject: [PATCH 09/88] optimize code --- Flow.Launcher/Storage/UserSelectedRecord.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs index b97138eb9..bc7a2da73 100644 --- a/Flow.Launcher/Storage/UserSelectedRecord.cs +++ b/Flow.Launcher/Storage/UserSelectedRecord.cs @@ -25,9 +25,9 @@ namespace Flow.Launcher.Storage public void Add(Result result) { var key = result.ToString(); - if (records.TryGetValue(key, out int value)) + if (records.ContainsKey(key)) { - records[key] = value + 1; + records[key]++; } else { From 5688f377e03fc8f58c1baba1934a164ef6b493f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 16 Jan 2021 02:34:26 +0800 Subject: [PATCH 10/88] Stop ProgressBar animation when Flow is hidden Co-Authored-By: imsh <9258159+imsh@users.noreply.github.com> --- Flow.Launcher/MainWindow.xaml | 2 +- Flow.Launcher/MainWindow.xaml.cs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index a2cfe569d..4cc0b4428 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -97,7 +97,7 @@ Background="Transparent"/> diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 3812b4e1f..87155ea17 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -84,8 +84,15 @@ namespace Flow.Launcher QueryTextBox.SelectAll(); _viewModel.LastQuerySelected = true; } + + ProgressBar.BeginStoryboard(_progressBarStoryboard); + } + else + { + _progressBarStoryboard.Stop(); } } + }; _settings.PropertyChanged += (o, e) => { From 114c12bdaf694dd2b5f98fc59b14ff1d1b84c0ad Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 18:47:19 +1100 Subject: [PATCH 11/88] formatting and description --- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- Flow.Launcher.Plugin/IAsyncPlugin.cs | 4 ++++ Flow.Launcher.Plugin/IPlugin.cs | 4 ++-- Flow.Launcher.Plugin/Interfaces/IReloadable.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 9 +++++++-- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 1 - 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5a5ba1971..26167e945 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -320,4 +320,4 @@ namespace Flow.Launcher.Core.Plugin } } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index 3e1a086bc..c6f11aead 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -12,6 +12,10 @@ namespace Flow.Launcher.Plugin /// /// Asynchronous Querying /// + /// + /// If the Querying or Init method requires high IO transmission + /// or performing CPU intense jobs (performing better with cancellation), please use this IAsyncPlugin interface + /// /// Query to search /// Cancel when querying job is obsolete /// diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 41c61a608..208f3021b 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -5,8 +5,8 @@ namespace Flow.Launcher.Plugin /// /// Synchronous Plugin Model for Flow Launcher /// - /// If you assume that Querying or Init method require IO transmission - /// or CPU Intense Job (performing better with cancellation), please try IAsyncPlugin interface + /// If the Querying or Init method requires high IO transmission + /// or performaing CPU intense jobs (performing better with cancellation), please try the IAsyncPlugin interface /// /// public interface IPlugin diff --git a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs index d9160b0ea..31611519c 100644 --- a/Flow.Launcher.Plugin/Interfaces/IReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IReloadable.cs @@ -12,7 +12,7 @@ /// it will call the plugins that have implemented this interface. /// /// - /// If requiring reloading data asynchrouly, please try IAsyncReloadable + /// If requiring reloading data asynchronously, please use the IAsyncReloadable interface /// /// public interface IReloadable diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index bcb257204..a062f59dc 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -408,8 +408,13 @@ namespace Flow.Launcher.ViewModel for (var i = 0; i < plugins.Count; i++) { if (!plugins[i].Metadata.Disabled) + { tasks[i] = QueryTask(plugins[i], query, currentCancellationToken); - else tasks[i] = Task.CompletedTask; // Avoid Null + } + else + { + tasks[i] = Task.CompletedTask; // Avoid Null + } } // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first @@ -429,7 +434,7 @@ namespace Flow.Launcher.ViewModel ProgressBarVisibility = Visibility.Hidden; } - // Local Function + // Local function async Task QueryTask(PluginPair plugin, Query query, CancellationToken token) { // Since it is wrapped within a Task.Run, the synchronous context is null diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 3253b7a7b..88d7d6927 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Windows; namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { From d741a98112a50b81cf4551037b1d6bad6c5b0be7 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 18:47:43 +1100 Subject: [PATCH 12/88] fixed last index time during init --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 954c238a9..c283faa83 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -97,22 +97,31 @@ namespace Flow.Launcher.Plugin.Program Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); }); + bool indexedWinApps = false; + bool indexedUWPApps = false; var a = Task.Run(() => { if (IsStartupIndexProgramsRequired || !_win32s.Any()) + { Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs); + indexedWinApps = true; + } }); var b = Task.Run(() => { if (IsStartupIndexProgramsRequired || !_uwps.Any()) + { Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexUwpPrograms); + indexedUWPApps = true; + } }); await Task.WhenAll(a, b); - _settings.LastIndexTime = DateTime.Today; + if (indexedWinApps && indexedUWPApps) + _settings.LastIndexTime = DateTime.Today; } public static void IndexWin32Programs() From 969265c24fb617f018ddf38285fb61ec1ac2274b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 19:05:28 +1100 Subject: [PATCH 13/88] fix formatting --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 12 +++++++----- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 3 +-- .../SuggestionSources/Bing.cs | 6 +++++- .../SuggestionSources/Google.cs | 6 +++++- .../SuggestionSources/SuggestionSource.cs | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index c283faa83..d7413874b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -202,12 +202,14 @@ namespace Flow.Launcher.Plugin.Program return; if (_uwps.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) - _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = - false; + _uwps.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier) + .FirstOrDefault() + .Enabled = false; if (_win32s.Any(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier)) - _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier).FirstOrDefault().Enabled = - false; + _win32s.Where(x => x.UniqueIdentifier == programToDelete.UniqueIdentifier) + .FirstOrDefault() + .Enabled = false; _settings.DisabledProgramSources .Add( @@ -241,4 +243,4 @@ namespace Flow.Launcher.Plugin.Program await IndexPrograms(); } } -} \ No newline at end of file +} diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 7ebab91a1..624fe05bc 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -170,8 +170,7 @@ namespace Flow.Launcher.Plugin.Sys // http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html // FYI, couldn't find documentation for this but if the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED)) // 0 for nothing - var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, - 0); + var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, 0); if (result != (uint) HRESULT.S_OK && result != (uint) 0x8000FFFF) { MessageBox.Show($"Error emptying recycle bin, error code: {result}\n" + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 81725c3f2..ffde2fda2 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -22,8 +22,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources try { const string api = "https://api.bing.com/qsonhs.aspx?q="; + using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); - if (resultStream.Length == 0) return new List(); // this handles the cancellation + + if (resultStream.Length == 0) + return new List(); // this handles the cancellation + json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS"); } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index b150d26c1..c33ebd7e1 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -21,8 +21,12 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources try { const string api = "https://www.google.com/complete/search?output=chrome&q="; + using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); - if (resultStream.Length == 0) return new List(); + + if (resultStream.Length == 0) + return new List(); + json = await JsonDocument.ParseAsync(resultStream); } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs index bf444a2f7..c58e61141 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/SuggestionSource.cs @@ -8,4 +8,4 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public abstract Task> Suggestions(string query, CancellationToken token); } -} \ No newline at end of file +} From 539f4bf4c46ca574a50dbb3f924b1b5160da7084 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 19:10:26 +1100 Subject: [PATCH 14/88] add eol --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 2 +- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 2 +- Flow.Launcher.Plugin/IAsyncPlugin.cs | 2 +- Flow.Launcher.Plugin/IPlugin.cs | 2 +- Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 1a1b17539..273698b86 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -54,4 +54,4 @@ namespace Flow.Launcher.Core.Plugin return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index b18c07e3c..fcf178445 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -183,4 +183,4 @@ namespace Flow.Launcher.Core.Plugin }); } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index c6f11aead..b0b41cc22 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -28,4 +28,4 @@ namespace Flow.Launcher.Plugin /// Task InitAsync(PluginInitContext context); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 208f3021b..203dc9af7 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -28,4 +28,4 @@ namespace Flow.Launcher.Plugin /// void Init(PluginInitContext context); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs index 434d83646..fc4ac4715 100644 --- a/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs +++ b/Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs @@ -17,4 +17,4 @@ namespace Flow.Launcher.Plugin { Task ReloadDataAsync(); } -} \ No newline at end of file +} From 4b9936bc90a12a3a177117d83f83ea22760e3acb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 17 Jan 2021 19:26:34 +1100 Subject: [PATCH 15/88] Version bump Flow.Launcher.Plugin Version bump Flow.Launcher.Plugin --- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index b7b52ac35..698802ba3 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -14,10 +14,10 @@ - 1.3.1 - 1.3.1 - 1.3.1 - 1.3.1 + 1.4.0 + 1.4.0 + 1.4.0 + 1.4.0 Flow.Launcher.Plugin Flow-Launcher MIT @@ -64,4 +64,4 @@ - \ No newline at end of file + From 3abc77f391d48b0ba15e1c65b4e9722149470b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 18:05:31 +0800 Subject: [PATCH 16/88] Merge remote-tracking branch 'upstream/dev' into RenderImprovement # Conflicts: # Flow.Launcher/ViewModel/MainViewModel.cs --- Flow.Launcher/ViewModel/MainViewModel.cs | 87 ++++++++---------------- 1 file changed, 28 insertions(+), 59 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 139d89169..35a7302f4 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -244,7 +244,7 @@ namespace Flow.Launcher.ViewModel public string QueryText { - get { return _queryText; } + get => _queryText; set { _queryText = value; @@ -444,28 +444,26 @@ namespace Flow.Launcher.ViewModel var plugins = PluginManager.ValidPluginsForQuery(query); Task.Run(async () => - { - if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) { - // Wait 45 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(45, currentCancellationToken); - if (currentCancellationToken.IsCancellationRequested) - return; - } - - _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => - { - // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet - if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) + if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) { - ProgressBarVisibility = Visibility.Visible; + // Wait 45 millisecond for query change in global query + // if query changes, return so that it won't be calculated + await Task.Delay(45, currentCancellationToken); + if (currentCancellationToken.IsCancellationRequested) + return; } - }, currentCancellationToken); - var plugins = PluginManager.ValidPluginsForQuery(query); - Task.Run(async () => - { + _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => + { + // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet + if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) + { + ProgressBarVisibility = Visibility.Visible; + } + }, currentCancellationToken); + + var plugins = PluginManager.ValidPluginsForQuery(query); // so looping will stop once it was cancelled Task[] tasks = new Task[plugins.Count]; @@ -475,7 +473,7 @@ namespace Flow.Launcher.ViewModel { if (!plugins[i].Metadata.Disabled) { - tasks[i] = QueryTask(plugins[i], query, currentCancellationToken); + tasks[i] = QueryTask(plugins[i]); } else { @@ -491,6 +489,9 @@ namespace Flow.Launcher.ViewModel // nothing to do here } + if (currentCancellationToken.IsCancellationRequested) + return; + // this should happen once after all queries are done so progress bar should continue // until the end of all querying _isQueryRunning = false; @@ -501,28 +502,20 @@ namespace Flow.Launcher.ViewModel } // Local function - async Task QueryTask(PluginPair plugin, Query query, CancellationToken token) + async Task QueryTask(PluginPair plugin) { // Since it is wrapped within a Task.Run, the synchronous context is null // Task.Yield will force it to run in ThreadPool await Task.Yield(); - var results = await PluginManager.QueryForPlugin(plugin, query, token); + var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); if (!currentCancellationToken.IsCancellationRequested) - UpdateResultView(results, plugin.Metadata, query); + _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, query, + currentCancellationToken)); } - }, currentCancellationToken).ContinueWith( - t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), + }, currentCancellationToken) + .ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), TaskContinuationOptions.OnlyOnFaulted); - - - else - - { - Results.Clear(); - Results.Visbility = Visibility.Collapsed; - } - } } @@ -786,7 +779,7 @@ namespace Flow.Launcher.ViewModel } else { - var priorityScore = metaResults.Metadata.Priority * 50; + var priorityScore = metaResults.Metadata.Priority * 150; result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore; } } @@ -795,30 +788,6 @@ namespace Flow.Launcher.ViewModel Results.AddResults(resultsForUpdates, token); } - /// U - /// To avoid deadlock, this method should not called from main thread - /// - public void UpdateResultView(List list, PluginMetadata metadata, Query originQuery) - { - foreach (var result in list) - { - if (_topMostRecord.IsTopMost(result)) - { - result.Score = int.MaxValue; - } - else - { - var priorityScore = metadata.Priority * 150; - result.Score += _userSelectedRecord.GetSelectedCount(result) * 5 + priorityScore; - } - } - - if (originQuery.RawQuery == _lastQuery.RawQuery) - { - Results.AddResults(list, metadata.ID); - } - } - #endregion } } \ No newline at end of file From 1071f75633305e41d27f155a9f60d359604f386d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 18:06:57 +0800 Subject: [PATCH 17/88] Remove unused using --- Flow.Launcher/ViewModel/MainViewModel.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 35a7302f4..2dcbfc004 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -19,9 +17,6 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; -using System.Windows.Media; -using Flow.Launcher.Infrastructure.Image; -using System.Collections.Concurrent; using Flow.Launcher.Infrastructure.Logger; using System.Threading.Tasks.Dataflow; From 501124609038dea1e4d06f6194edbdd5f71fbb5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 18:10:36 +0800 Subject: [PATCH 18/88] Add scoring to ContextMenu search --- Flow.Launcher/ViewModel/MainViewModel.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2dcbfc004..454d934a3 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -354,9 +354,20 @@ namespace Flow.Launcher.ViewModel { var filtered = results.Where ( - r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet() - || StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet() - ).ToList(); + r => + { + var match = StringMatcher.FuzzySearch(query, r.Title); + if (!match.IsSearchPrecisionScoreMet()) + { + match = StringMatcher.FuzzySearch(query, r.SubTitle); + } + + if (!match.IsSearchPrecisionScoreMet()) return false; + + r.Score = match.Score; + return true; + + }).ToList(); ContextMenu.AddResults(filtered, id); } else From e2e3f4a365acef01ca9fc182bc91543c49f15adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 18:21:35 +0800 Subject: [PATCH 19/88] remove extra query building due to merge --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 454d934a3..8195c745a 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -469,9 +469,6 @@ namespace Flow.Launcher.ViewModel } }, currentCancellationToken); - var plugins = PluginManager.ValidPluginsForQuery(query); - // so looping will stop once it was cancelled - Task[] tasks = new Task[plugins.Count]; try { From bf96a60862307c41e91bb960a874c15ed1818cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 18:39:53 +0800 Subject: [PATCH 20/88] Only wait PluginManifest downloading for 500ms to allow Flow to be used even without internet connection. --- .../Main.cs | 17 +++- .../PluginsManager.cs | 81 +++++++++++-------- 2 files changed, 62 insertions(+), 36 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 40579e6e5..1bea7d1be 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -37,8 +37,17 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = viewModel.Settings; contextMenu = new ContextMenu(Context); pluginManager = new PluginsManager(Context, Settings); - await pluginManager.UpdateManifest(); - lastUpdateTime = DateTime.Now; + var updateManifestTask = pluginManager.UpdateManifest(); + if (await Task.WhenAny(updateManifestTask, Task.Delay(500)) == updateManifestTask) + { + lastUpdateTime = DateTime.Now; + } + else + { + context.API.ShowMsg("Plugin Manifest Download Fail.", + @"Please check internet transmission with Github.com. + You may not be able to Install and Update Plugin.", pluginManager.icoPath); + } } public List LoadContextMenus(Result selectedResult) @@ -61,7 +70,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return search switch { - var s when s.StartsWith(Settings.HotKeyInstall) => pluginManager.RequestInstallOrUpdate(s), + var s when s.StartsWith(Settings.HotKeyInstall) => await pluginManager.RequestInstallOrUpdate(s), var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s), var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s), _ => pluginManager.GetDefaultHotKeys().Where(hotkey => @@ -93,4 +102,4 @@ namespace Flow.Launcher.Plugin.PluginsManager lastUpdateTime = DateTime.Now; } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 68df5bc1f..a02cce339 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -36,7 +37,7 @@ namespace Flow.Launcher.Plugin.PluginsManager } } - private readonly string icoPath = "Images\\pluginsmanager.png"; + internal readonly string icoPath = "Images\\pluginsmanager.png"; internal PluginsManager(PluginInitContext context, Settings settings) { @@ -64,27 +65,27 @@ namespace Flow.Launcher.Plugin.PluginsManager return false; } }, - new Result() + new Result() + { + Title = Settings.HotkeyUninstall, + IcoPath = icoPath, + Action = _ => { - Title = Settings.HotkeyUninstall, - IcoPath = icoPath, - Action = _ => - { - Context.API.ChangeQuery("pm uninstall "); - return false; - } - }, - new Result() - { - Title = Settings.HotkeyUpdate, - IcoPath = icoPath, - Action = _ => - { - Context.API.ChangeQuery("pm update "); - return false; - } + Context.API.ChangeQuery("pm uninstall "); + return false; } - }; + }, + new Result() + { + Title = Settings.HotkeyUpdate, + IcoPath = icoPath, + Action = _ => + { + Context.API.ChangeQuery("pm update "); + return false; + } + } + }; } internal async Task InstallOrUpdate(UserPlugin plugin) @@ -137,7 +138,8 @@ namespace Flow.Launcher.Plugin.PluginsManager catch (Exception e) { Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), - string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), plugin.Name)); + string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), + plugin.Name)); Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "InstallOrUpdate"); @@ -164,7 +166,8 @@ namespace Flow.Launcher.Plugin.PluginsManager from existingPlugin in Context.API.GetAllPlugins() join pluginFromManifest in pluginsManifest.UserPlugins on existingPlugin.Metadata.ID equals pluginFromManifest.ID - where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) < 0 // if current version precedes manifest version + where existingPlugin.Metadata.Version.CompareTo(pluginFromManifest.Version) < + 0 // if current version precedes manifest version select new { @@ -214,22 +217,29 @@ namespace Flow.Launcher.Plugin.PluginsManager Task.Run(async delegate { - Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), - Context.API.GetTranslation("plugin_pluginsmanager_please_wait")); + Context.API.ShowMsg( + Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), + Context.API.GetTranslation("plugin_pluginsmanager_please_wait")); - await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath).ConfigureAwait(false); + await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath) + .ConfigureAwait(false); - Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), - Context.API.GetTranslation("plugin_pluginsmanager_download_success")); + Context.API.ShowMsg( + Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), + Context.API.GetTranslation("plugin_pluginsmanager_download_success")); Install(x.PluginNewUserPlugin, downloadToFilePath); Context.API.RestartApp(); }).ContinueWith(t => { - Log.Exception("PluginsManager", $"Update failed for {x.Name}", t.Exception.InnerException, "RequestUpdate"); - Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), - string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), x.Name)); + Log.Exception("PluginsManager", $"Update failed for {x.Name}", + t.Exception.InnerException, "RequestUpdate"); + Context.API.ShowMsg( + Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), + string.Format( + Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), + x.Name)); }, TaskContinuationOptions.OnlyOnFaulted); return true; @@ -264,8 +274,15 @@ namespace Flow.Launcher.Plugin.PluginsManager .ToList(); } - internal List RequestInstallOrUpdate(string searchName) + private Task _downloadManifestTask = Task.CompletedTask; + + internal async ValueTask> RequestInstallOrUpdate(string searchName) { + if (!pluginsManifest.UserPlugins.Any() && _downloadManifestTask.IsCompleted) + _downloadManifestTask = pluginsManifest.DownloadManifest(); + + await _downloadManifestTask; + var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); var results = @@ -406,4 +423,4 @@ namespace Flow.Launcher.Plugin.PluginsManager return new List(); } } -} +} \ No newline at end of file From d203a7a5f3d374b62a752878b4156be2de095c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 18:42:01 +0800 Subject: [PATCH 21/88] Add Cancellation Token --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- .../PluginsManager.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 1bea7d1be..1d473a82a 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -70,7 +70,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return search switch { - var s when s.StartsWith(Settings.HotKeyInstall) => await pluginManager.RequestInstallOrUpdate(s), + var s when s.StartsWith(Settings.HotKeyInstall) => await pluginManager.RequestInstallOrUpdate(s, token), var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s), var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s), _ => pluginManager.GetDefaultHotKeys().Where(hotkey => diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index a02cce339..06e97438e 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -276,13 +276,18 @@ namespace Flow.Launcher.Plugin.PluginsManager private Task _downloadManifestTask = Task.CompletedTask; - internal async ValueTask> RequestInstallOrUpdate(string searchName) + internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token) { - if (!pluginsManifest.UserPlugins.Any() && _downloadManifestTask.IsCompleted) + if (!pluginsManifest.UserPlugins.Any() && + _downloadManifestTask.IsCompleted || + _downloadManifestTask.IsFaulted) _downloadManifestTask = pluginsManifest.DownloadManifest(); await _downloadManifestTask; + if (token.IsCancellationRequested) + return null; + var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); var results = From 415adb72bf8209bea6b18f5e64a4af9d83fb4425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 18:46:08 +0800 Subject: [PATCH 22/88] Optimize Logic --- .../Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 06e97438e..0f5e6d9e8 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -279,15 +279,16 @@ namespace Flow.Launcher.Plugin.PluginsManager internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token) { if (!pluginsManifest.UserPlugins.Any() && - _downloadManifestTask.IsCompleted || - _downloadManifestTask.IsFaulted) + _downloadManifestTask.Status != TaskStatus.Running) + { _downloadManifestTask = pluginsManifest.DownloadManifest(); + } await _downloadManifestTask; if (token.IsCancellationRequested) return null; - + var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); var results = From 2fecfa2644d7675ff527b978299945b12d0f20d3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 Jan 2021 07:44:43 +1100 Subject: [PATCH 23/88] verison bump plugins manager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 60ab7ab18..3accd4fb9 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.5.0", + "Version": "1.5.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From ff26bccba5c27daea0222c44b9937461c094c0d8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 18 Jan 2021 07:58:38 +1100 Subject: [PATCH 24/88] version bump plugins --- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.Sys/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index aa44c4413..68ab9f562 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.2.6", + "Version": "1.3.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 3accd4fb9..7f3f4a5cf 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.5.1", + "Version": "1.6.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index 6c2c18e47..1dedd9909 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.2.3", + "Version": "1.3.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json index cf8ed6041..75d7073b8 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json @@ -4,7 +4,7 @@ "Name": "System Commands", "Description": "Provide System related commands. e.g. shutdown,lock,setting etc.", "Author": "qianlifeng", - "Version": "1.1.2", + "Version": "1.2.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll", diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index c036fbf8b..2696ab622 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -25,7 +25,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.2.1", + "Version": "1.3.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From 2643c7d7302d76b0d8737b29d0b456ca7f24cb2b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 Jan 2021 07:05:30 +1100 Subject: [PATCH 25/88] exclude index search on special chars --- .../Search/WindowsIndex/IndexSearch.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 5b1d47ef8..06059ceea 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Logger; using Microsoft.Search.Interop; using System; using System.Collections.Generic; @@ -15,7 +15,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex private readonly ResultManager resultManager; // Reserved keywords in oleDB - private readonly string reservedStringPattern = @"^[\/\\\$\%_]+$"; + private readonly string reservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_]+$"; internal IndexSearch(PluginInitContext context) { diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs index e62ea93fc..e62637896 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Settings.cs @@ -1,7 +1,6 @@ using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.FolderLinks; using System.Collections.Generic; -using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin.Explorer { From ed1bf0e956fe2b713d2715252a6636a98ecc857e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 Jan 2021 07:07:06 +1100 Subject: [PATCH 26/88] version bump Explorer plugin --- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index aa44c4413..49baa5090 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.2.6", + "Version": "1.2.7", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", From 2463582af01e6b7a4f8234bd0a121406733afcc0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 19 Jan 2021 08:52:12 +1100 Subject: [PATCH 27/88] fix error message not working + increase delay --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 1d473a82a..932248de4 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -38,15 +38,15 @@ namespace Flow.Launcher.Plugin.PluginsManager contextMenu = new ContextMenu(Context); pluginManager = new PluginsManager(Context, Settings); var updateManifestTask = pluginManager.UpdateManifest(); - if (await Task.WhenAny(updateManifestTask, Task.Delay(500)) == updateManifestTask) + if (await Task.WhenAny(updateManifestTask, Task.Delay(1000)) == updateManifestTask) { lastUpdateTime = DateTime.Now; } else { context.API.ShowMsg("Plugin Manifest Download Fail.", - @"Please check internet transmission with Github.com. - You may not be able to Install and Update Plugin.", pluginManager.icoPath); + "Please check if you can connect to github.com. "+ + "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); } } From d7abae1ab22166eabf940d3983400aadd9ba93d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 19 Jan 2021 10:08:37 +0800 Subject: [PATCH 28/88] Add Cancellation token for file system enumeration --- .../DirectoryInfo/DirectoryInfoSearch.cs | 42 +++++++++++-------- .../Search/SearchManager.cs | 20 ++++----- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 88d7d6927..efdb5a6fe 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { @@ -16,14 +17,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo resultManager = new ResultManager(context); } - internal List TopLevelDirectorySearch(Query query, string search) + internal List TopLevelDirectorySearch(Query query, string search, CancellationToken token) { var criteria = ConstructSearchCriteria(search); - if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > search.LastIndexOf(Constants.DirectorySeperator)) - return DirectorySearch(SearchOption.AllDirectories, query, search, criteria); + if (search.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > + search.LastIndexOf(Constants.DirectorySeperator)) + return DirectorySearch(new EnumerationOptions + { + RecurseSubdirectories = true + }, query, search, criteria, token); - return DirectorySearch(SearchOption.TopDirectoryOnly, query, search, criteria); + return DirectorySearch(null, query, search, criteria, token); // null will be passed as default } public string ConstructSearchCriteria(string search) @@ -45,7 +50,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo return incompleteName; } - private List DirectorySearch(SearchOption searchOption, Query query, string search, string searchCriteria) + private List DirectorySearch(EnumerationOptions enumerationOption, Query query, string search, + string searchCriteria, CancellationToken token) { var results = new List(); @@ -58,38 +64,38 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo { var directoryInfo = new System.IO.DirectoryInfo(path); - foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, searchOption)) + foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption)) { - if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue; - if (fileSystemInfo is System.IO.DirectoryInfo) { - folderList.Add(resultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, fileSystemInfo.FullName, query, true, false)); + folderList.Add(resultManager.CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, + fileSystemInfo.FullName, query, true, false)); } else { fileList.Add(resultManager.CreateFileResult(fileSystemInfo.FullName, query, true, false)); } + + if (token.IsCancellationRequested) + return null; } } catch (Exception e) { - if (e is UnauthorizedAccessException || e is ArgumentException) - { - results.Add(new Result { Title = e.Message, Score = 501 }); + if (!(e is ArgumentException)) throw e; + + results.Add(new Result {Title = e.Message, Score = 501}); - return results; - } + return results; #if DEBUG // Please investigate and handle error from DirectoryInfo search - throw e; #else Log.Exception($"|Flow.Launcher.Plugin.Explorer.DirectoryInfoSearch|Error from performing DirectoryInfoSearch", e); -#endif +#endif } - // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. + // Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList(); } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 6b3a96912..d51833a6f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -81,12 +81,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search return null; results.AddRange(await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync, - DirectoryInfoClassSearch, - useIndexSearch, - query, - locationPath, - token).ConfigureAwait(false)); - + DirectoryInfoClassSearch, + useIndexSearch, + query, + locationPath, + token).ConfigureAwait(false)); + return results; } @@ -109,23 +109,23 @@ namespace Flow.Launcher.Plugin.Explorer.Search return actionKeyword == settings.FileContentSearchActionKeyword; } - private List DirectoryInfoClassSearch(Query query, string querySearch) + private List DirectoryInfoClassSearch(Query query, string querySearch, CancellationToken token) { var directoryInfoSearch = new DirectoryInfoSearch(context); - return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch); + return directoryInfoSearch.TopLevelDirectorySearch(query, querySearch, token); } public async Task> TopLevelDirectorySearchBehaviourAsync( Func>> windowsIndexSearch, - Func> directoryInfoClassSearch, + Func> directoryInfoClassSearch, bool useIndexSearch, Query query, string querySearchString, CancellationToken token) { if (!useIndexSearch) - return directoryInfoClassSearch(query, querySearchString); + return directoryInfoClassSearch(query, querySearchString, token); return await windowsIndexSearch(query, querySearchString, token); } From 07309568226695e73f46e44d38531385b9e1405f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 19 Jan 2021 22:42:32 +0800 Subject: [PATCH 29/88] Make pause and resume when progressbarvisibility changed. --- Flow.Launcher/MainWindow.xaml.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 87155ea17..114b6cd7f 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -52,7 +52,7 @@ namespace Flow.Launcher private void OnInitialized(object sender, EventArgs e) { - + } private void OnLoaded(object sender, RoutedEventArgs _) @@ -85,14 +85,27 @@ namespace Flow.Launcher _viewModel.LastQuerySelected = true; } - ProgressBar.BeginStoryboard(_progressBarStoryboard); } else { - _progressBarStoryboard.Stop(); } } - + else if (e.PropertyName == nameof(MainViewModel.ProgressBarVisibility)) + { + Dispatcher.Invoke(() => + { + if (ProgressBar.Visibility == Visibility.Hidden) + { + _progressBarStoryboard.Pause(); + } + else + { + _progressBarStoryboard.Resume(); + } + }, System.Windows.Threading.DispatcherPriority.Render); + + } + }; _settings.PropertyChanged += (o, e) => { From 4f12b80603fabea748fa1c0baf2f08d901656995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 19 Jan 2021 22:46:18 +0800 Subject: [PATCH 30/88] fix testing --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 09c7d9a30..e7f96fc87 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -24,7 +24,7 @@ namespace Flow.Launcher.Test.Plugins return new List(); } - private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString) + private List MethodDirectoryInfoClassSearchReturnsTwoResults(Query dummyQuery, string dummyString, CancellationToken token) { return new List { From 89443bace76f44fb0551f3f63d70a8779868b92a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 20 Jan 2021 06:34:26 +1100 Subject: [PATCH 31/88] fix formatting --- Flow.Launcher.Infrastructure/Image/ImageLoader.cs | 1 - Flow.Launcher/ViewModel/ResultViewModel.cs | 4 ---- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 1 - Flow.Launcher/ViewModel/ResultsViewModel.cs | 15 --------------- 4 files changed, 21 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 73f565a33..ac333d567 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -39,7 +39,6 @@ namespace Flow.Launcher.Infrastructure.Image var usage = LoadStorageToConcurrentDictionary(); - foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) { ImageSource img = new BitmapImage(new Uri(icon)); diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index fa0c51a65..c91bbb107 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -1,14 +1,11 @@ using System; -using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; -using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Microsoft.FSharp.Core; namespace Flow.Launcher.ViewModel { @@ -128,7 +125,6 @@ namespace Flow.Launcher.ViewModel } } - public override int GetHashCode() { return Result.GetHashCode(); diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index 2257d35b0..be48f53c1 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -23,7 +23,6 @@ namespace Flow.Launcher.ViewModel Token = token; } - public ResultsForUpdate(List results, PluginMetadata metadata, Query query, CancellationToken token) { Results = results; diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 932d6fc4b..1b8dd602d 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; -using System.ComponentModel; -using System.Configuration; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -11,7 +9,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; -using System.Windows.Forms; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; @@ -137,13 +134,11 @@ namespace Flow.Launcher.ViewModel Results.Update(Results.Where(r => r.Result.PluginID != metadata.ID).ToList()); } - /// /// To avoid deadlock, this method should not called from main thread /// public void AddResults(List newRawResults, string resultId) { - lock (_collectionLock) { var newResults = NewResults(newRawResults, resultId); @@ -193,8 +188,6 @@ namespace Flow.Launcher.ViewModel Results.Update(newResults, token); if (Results.Any()) SelectedItem = Results[0]; - - } switch (Visbility) @@ -212,7 +205,6 @@ namespace Flow.Launcher.ViewModel } - private List NewResults(List newRawResults, string resultId) { if (newRawResults.Count == 0) @@ -222,8 +214,6 @@ namespace Flow.Launcher.ViewModel var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)).ToList(); - - return results.Where(r => r.Result.PluginID != resultId) .Concat(results.Intersect(newResults).Union(newResults)) .OrderByDescending(r => r.Result.Score) @@ -245,7 +235,6 @@ namespace Flow.Launcher.ViewModel } #endregion - #region FormattedText Dependency Property public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached( "FormattedText", @@ -279,7 +268,6 @@ namespace Flow.Launcher.ViewModel public class ResultCollection : ObservableCollection { - private long editTime = 0; private bool _suppressNotifying = false; @@ -323,9 +311,6 @@ namespace Flow.Launcher.ViewModel ClearItems(); } - - - /// /// Update the results collection with new results, try to keep identical results /// From cd92512fe5b1cd6ff58b691077283164f82613c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 20 Jan 2021 13:47:25 +0800 Subject: [PATCH 32/88] Optimize code --- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 2 +- .../Search/SearchManager.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index efdb5a6fe..d15069981 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -28,7 +28,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo RecurseSubdirectories = true }, query, search, criteria, token); - return DirectorySearch(null, query, search, criteria, token); // null will be passed as default + return DirectorySearch(new EnumerationOptions(), query, search, criteria, token); // null will be passed as default } public string ConstructSearchCriteria(string search) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index d51833a6f..6c9b81c88 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -80,12 +80,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (token.IsCancellationRequested) return null; - results.AddRange(await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync, + var directoryResult = await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync, DirectoryInfoClassSearch, useIndexSearch, query, locationPath, - token).ConfigureAwait(false)); + token).ConfigureAwait(false); + + if (token.IsCancellationRequested) + return null; + + results.AddRange(directoryResult); return results; } From 1aa119d672af4ee1c13250902a8936bd5c02bccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 20 Jan 2021 13:49:50 +0800 Subject: [PATCH 33/88] fix some legacy code from #195 --- Flow.Launcher/PublicAPIInstance.cs | 1 + Flow.Launcher/ViewModel/ResultsViewModel.cs | 49 +++++---------------- 2 files changed, 12 insertions(+), 38 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 17673a62a..89e4fab3b 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -92,6 +92,7 @@ namespace Flow.Launcher { Application.Current.Dispatcher.Invoke(() => { + useMainWindowAsOwner = false; var msg = useMainWindowAsOwner ? new Msg { Owner = Application.Current.MainWindow } : new Msg(); msg.Show(title, subTitle, iconPath); }); diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 1b8dd602d..f04304267 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -139,39 +139,9 @@ namespace Flow.Launcher.ViewModel /// public void AddResults(List newRawResults, string resultId) { - lock (_collectionLock) - { - var newResults = NewResults(newRawResults, resultId); + var newResults = NewResults(newRawResults, resultId); - // https://social.msdn.microsoft.com/Forums/vstudio/en-US/5ff71969-f183-4744-909d-50f7cd414954/binding-a-tabcontrols-selectedindex-not-working?forum=wpf - // fix selected index flow - var updateTask = Task.Run(() => - { - // update UI in one run, so it can avoid UI flickering - - Results.Update(newResults); - if (Results.Any()) - SelectedItem = Results[0]; - }); - if (!updateTask.Wait(300)) - { - updateTask.Dispose(); - throw new TimeoutException("Update result use too much time."); - } - - } - - if (Visbility != Visibility.Visible && Results.Count > 0) - { - Margin = new Thickness { Top = 8 }; - SelectedIndex = 0; - Visbility = Visibility.Visible; - } - else - { - Margin = new Thickness { Top = 0 }; - Visbility = Visibility.Collapsed; - } + UpdateResults(newResults); } /// /// To avoid deadlock, this method should not called from main thread @@ -181,10 +151,15 @@ namespace Flow.Launcher.ViewModel var newResults = NewResults(resultsForUpdates); if (token.IsCancellationRequested) return; + UpdateResults(newResults, token); + + } + + private void UpdateResults(List newResults, CancellationToken token = default) + { lock (_collectionLock) { // update UI in one run, so it can avoid UI flickering - Results.Update(newResults, token); if (Results.Any()) SelectedItem = Results[0]; @@ -202,7 +177,6 @@ namespace Flow.Launcher.ViewModel Visbility = Visibility.Collapsed; break; } - } private List NewResults(List newRawResults, string resultId) @@ -212,10 +186,10 @@ namespace Flow.Launcher.ViewModel var results = Results as IEnumerable; - var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)).ToList(); + var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)); return results.Where(r => r.Result.PluginID != resultId) - .Concat(results.Intersect(newResults).Union(newResults)) + .Concat(newResults) .OrderByDescending(r => r.Result.Score) .ToList(); } @@ -228,8 +202,7 @@ namespace Flow.Launcher.ViewModel var results = Results as IEnumerable; return results.Where(r => r != null && !resultsForUpdates.Any(u => u.Metadata.ID == r.Result.PluginID)) - .Concat( - resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings))) + .Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings))) .OrderByDescending(rv => rv.Result.Score) .ToList(); } From a42c9c0ac17ea8af242853883129fd5584534543 Mon Sep 17 00:00:00 2001 From: adamjijo Date: Wed, 20 Jan 2021 11:26:24 +0530 Subject: [PATCH 34/88] Added themes. Added Nord and Nord Darker themes. --- Flow.Launcher/Themes/Nord Darker.xaml | 56 +++++++++++++++++++++++++++ Flow.Launcher/Themes/Nord.xaml | 56 +++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 Flow.Launcher/Themes/Nord Darker.xaml create mode 100644 Flow.Launcher/Themes/Nord.xaml diff --git a/Flow.Launcher/Themes/Nord Darker.xaml b/Flow.Launcher/Themes/Nord Darker.xaml new file mode 100644 index 000000000..13f4bf230 --- /dev/null +++ b/Flow.Launcher/Themes/Nord Darker.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + #5e81ac + + + + diff --git a/Flow.Launcher/Themes/Nord.xaml b/Flow.Launcher/Themes/Nord.xaml new file mode 100644 index 000000000..2253b3410 --- /dev/null +++ b/Flow.Launcher/Themes/Nord.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + #5e81ac + + + + From 9d126df225222f41f6e23d9aa4b0601ce470f920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 20 Jan 2021 17:49:39 +0800 Subject: [PATCH 35/88] Use List replace ObservableCollection to have control toward Capacity --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 53 +++++++++++---------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index f04304267..55dea7440 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -239,49 +239,50 @@ namespace Flow.Launcher.ViewModel } #endregion - public class ResultCollection : ObservableCollection + public class ResultCollection : List, INotifyCollectionChanged { private long editTime = 0; - private bool _suppressNotifying = false; - private CancellationToken _token; - protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + public event NotifyCollectionChangedEventHandler CollectionChanged; + + protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { - if (!_suppressNotifying) - { - base.OnCollectionChanged(e); - } + CollectionChanged(this, e); } - public void BulkAddRange(IEnumerable resultViews) + public void BulkAddAll(List resultViews) { - // suppress notifying before adding all element - _suppressNotifying = true; - foreach (var item in resultViews) - { - Add(item); - } - _suppressNotifying = false; - // manually update event - // wpf use directx / double buffered already, so just reset all won't cause ui flickering + AddRange(resultViews); + + // can return because the list will be cleared next time updated, which include a reset event if (_token.IsCancellationRequested) return; + + // manually update event + // wpf use directx / double buffered already, so just reset all won't cause ui flickering OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } - public void AddRange(IEnumerable Items) + private void AddAll(List Items) { foreach (var item in Items) { if (_token.IsCancellationRequested) return; Add(item); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } } - public void RemoveAll() + public void RemoveAll(int Capacity = 512) { - ClearItems(); + Clear(); + if (this.Capacity > 8000) + { + this.Capacity = Capacity; + + } + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// @@ -296,15 +297,19 @@ namespace Flow.Launcher.ViewModel if (editTime < 10 || newItems.Count < 30) { - if (Count != 0) ClearItems(); - AddRange(newItems); + if (Count != 0) RemoveAll(newItems.Count); + AddAll(newItems); editTime++; return; } else { Clear(); - BulkAddRange(newItems); + BulkAddAll(newItems); + if (Capacity > 8000 && newItems.Count < 3000) + { + Capacity = newItems.Count; + } editTime++; } } From 912cca6c5f62df9c6fcca505f94b2c1300a4905b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 20 Jan 2021 18:00:16 +0800 Subject: [PATCH 36/88] Use callback to check whether downloading manifest successfully. --- .../Main.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 932248de4..c8680371c 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -30,7 +30,7 @@ namespace Flow.Launcher.Plugin.PluginsManager return new PluginsManagerSettings(viewModel); } - public async Task InitAsync(PluginInitContext context) + public Task InitAsync(PluginInitContext context) { Context = context; viewModel = new SettingsViewModel(context); @@ -38,16 +38,19 @@ namespace Flow.Launcher.Plugin.PluginsManager contextMenu = new ContextMenu(Context); pluginManager = new PluginsManager(Context, Settings); var updateManifestTask = pluginManager.UpdateManifest(); - if (await Task.WhenAny(updateManifestTask, Task.Delay(1000)) == updateManifestTask) + _ = updateManifestTask.ContinueWith(t => { - lastUpdateTime = DateTime.Now; - } - else - { - context.API.ShowMsg("Plugin Manifest Download Fail.", - "Please check if you can connect to github.com. "+ + if (t.IsCompletedSuccessfully) + lastUpdateTime = DateTime.Now; + else + { + context.API.ShowMsg("Plugin Manifest Download Fail.", + "Please check if you can connect to github.com. " + "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); - } + } + }); + + return Task.CompletedTask; } public List LoadContextMenus(Result selectedResult) From 2ca2041ed7938f1d0de8cac5efa6ea501d17b014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 20 Jan 2021 19:07:12 +0800 Subject: [PATCH 37/88] Increase opacity for blurblack and blurwhite --- Flow.Launcher/Themes/BlurBlack.xaml | 4 ++-- Flow.Launcher/Themes/BlurWhite.xaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Themes/BlurBlack.xaml b/Flow.Launcher/Themes/BlurBlack.xaml index dc80c144b..5c615d500 100644 --- a/Flow.Launcher/Themes/BlurBlack.xaml +++ b/Flow.Launcher/Themes/BlurBlack.xaml @@ -19,7 +19,7 @@ @@ -28,7 +28,7 @@ - + diff --git a/Flow.Launcher/Themes/BlurWhite.xaml b/Flow.Launcher/Themes/BlurWhite.xaml index 1c1f2f9ec..6a130bb39 100644 --- a/Flow.Launcher/Themes/BlurWhite.xaml +++ b/Flow.Launcher/Themes/BlurWhite.xaml @@ -17,7 +17,7 @@ @@ -26,7 +26,7 @@ - + From 258de0109e9ff4567bff5a147762265e69d9b526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 20 Jan 2021 19:07:38 +0800 Subject: [PATCH 38/88] Allign Gray and Light design with Darker --- Flow.Launcher/Themes/Gray.xaml | 20 +++++++++----------- Flow.Launcher/Themes/Light.xaml | 12 ++++++------ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher/Themes/Gray.xaml b/Flow.Launcher/Themes/Gray.xaml index 16a1db274..1fbaa959a 100644 --- a/Flow.Launcher/Themes/Gray.xaml +++ b/Flow.Launcher/Themes/Gray.xaml @@ -4,21 +4,19 @@ @@ -31,15 +29,15 @@ - #00AAF6 + #787878 @@ -38,7 +38,7 @@ - #3875D7 + #909090 + + + + + + + + + + + + + + + #356ef3 + + + + + + diff --git a/Flow.Launcher/Themes/BlurBlack.xaml b/Flow.Launcher/Themes/BlurBlack.xaml index 5c615d500..dc80c144b 100644 --- a/Flow.Launcher/Themes/BlurBlack.xaml +++ b/Flow.Launcher/Themes/BlurBlack.xaml @@ -19,7 +19,7 @@ @@ -28,7 +28,7 @@ - + From 4e7c9a76e8b8ec1d90f9229a9fd8c21719f2d99c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 21 Jan 2021 13:38:26 +1100 Subject: [PATCH 40/88] version bump PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 7f3f4a5cf..75d6038d4 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.6.0", + "Version": "1.6.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From 49d5faee9eca3df4018dbb1b8b845771e860d7c9 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 21 Jan 2021 13:41:50 +1100 Subject: [PATCH 41/88] formatting --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index c8680371c..66bfd2ab5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -41,7 +41,9 @@ namespace Flow.Launcher.Plugin.PluginsManager _ = updateManifestTask.ContinueWith(t => { if (t.IsCompletedSuccessfully) + { lastUpdateTime = DateTime.Now; + } else { context.API.ShowMsg("Plugin Manifest Download Fail.", From 5d1790cb0eda18afb6de232ce8cfd198f2905c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 21 Jan 2021 12:11:01 +0800 Subject: [PATCH 42/88] change visibility from ProgressBar.Visibility to _viewModel.ProgressBarVisibility --- Flow.Launcher/MainWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 114b6cd7f..96efda94d 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -94,7 +94,7 @@ namespace Flow.Launcher { Dispatcher.Invoke(() => { - if (ProgressBar.Visibility == Visibility.Hidden) + if (_viewModel.ProgressBarVisibility == Visibility.Hidden) { _progressBarStoryboard.Pause(); } From e0c345ae130be3010c4627aed3f133004f44fcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 21 Jan 2021 12:18:23 +0800 Subject: [PATCH 43/88] removing legacy code for testing --- Flow.Launcher/PublicAPIInstance.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 89e4fab3b..17673a62a 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -92,7 +92,6 @@ namespace Flow.Launcher { Application.Current.Dispatcher.Invoke(() => { - useMainWindowAsOwner = false; var msg = useMainWindowAsOwner ? new Msg { Owner = Application.Current.MainWindow } : new Msg(); msg.Show(title, subTitle, iconPath); }); From 8311b39ddc034f73ac11e2c7185067cbaebe00f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 21 Jan 2021 18:35:24 +0800 Subject: [PATCH 44/88] Add null check for OnCollectionChanged --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 55dea7440..2a0818b7d 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -249,7 +249,7 @@ namespace Flow.Launcher.ViewModel protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { - CollectionChanged(this, e); + CollectionChanged?.Invoke(this, e); } public void BulkAddAll(List resultViews) From 5389763f5801c989b68d7ec1df7f1a826ebc3389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 21 Jan 2021 18:38:02 +0800 Subject: [PATCH 45/88] Add another check to avoid some corner case --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 2a0818b7d..4a4a45f11 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -247,6 +247,7 @@ namespace Flow.Launcher.ViewModel public event NotifyCollectionChangedEventHandler CollectionChanged; + protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { CollectionChanged?.Invoke(this, e); @@ -277,7 +278,7 @@ namespace Flow.Launcher.ViewModel public void RemoveAll(int Capacity = 512) { Clear(); - if (this.Capacity > 8000) + if (this.Capacity > 8000 && Capacity < this.Capacity) { this.Capacity = Capacity; From 1a758c391922b70259538848d9a646e195c2a396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 21 Jan 2021 19:39:18 +0800 Subject: [PATCH 46/88] Use Token.throwifCancellationRequested --- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 3 +-- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index d15069981..779827b6d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -76,8 +76,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo fileList.Add(resultManager.CreateFileResult(fileSystemInfo.FullName, query, true, false)); } - if (token.IsCancellationRequested) - return null; + token.ThrowIfCancellationRequested(); } } catch (Exception e) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 6c9b81c88..7e3bf7776 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -77,8 +77,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search results.Add(resultManager.CreateOpenCurrentFolderResult(locationPath, useIndexSearch)); - if (token.IsCancellationRequested) - return null; + token.ThrowIfCancellationRequested(); var directoryResult = await TopLevelDirectorySearchBehaviourAsync(WindowsIndexTopLevelFolderSearchAsync, DirectoryInfoClassSearch, @@ -87,11 +86,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search locationPath, token).ConfigureAwait(false); - if (token.IsCancellationRequested) - return null; + token.ThrowIfCancellationRequested(); results.AddRange(directoryResult); - + return results; } From f388b75d2610c92e1128edfba55667a80e53f6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 21 Jan 2021 19:51:22 +0800 Subject: [PATCH 47/88] Add index when calling NotifyCollectionChangeAction.Add --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 4a4a45f11..4808e0ee0 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -267,12 +267,13 @@ namespace Flow.Launcher.ViewModel } private void AddAll(List Items) { - foreach (var item in Items) + for (int i = 0; i < Items.Count; i++) { + var item = Items[i]; if (_token.IsCancellationRequested) return; Add(item); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i)); } } public void RemoveAll(int Capacity = 512) From b426dd10d1b1b6b60d4c40b85fbdc8444675d708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 22 Jan 2021 16:19:03 +0800 Subject: [PATCH 48/88] Rewrite LocationPathString match --- .../SharedCommands/FilesFolders.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 27cd1a558..a185b2a1c 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Text.RegularExpressions; using System.Windows; namespace Flow.Launcher.Plugin.SharedCommands @@ -142,35 +143,28 @@ namespace Flow.Launcher.Plugin.SharedCommands Process.Start(FileExplorerProgramEXE, $" /select,\"{path}\""); } + /// /// This checks whether a given string is a directory path or network location string. /// It does not check if location actually exists. /// public static bool IsLocationPathString(string querySearchString) { - if (string.IsNullOrEmpty(querySearchString)) + if (string.IsNullOrEmpty(querySearchString) || querySearchString.Length < 3) return false; // // shared folder location, and not \\\location\ - if (querySearchString.Length >= 3 - && querySearchString.StartsWith(@"\\") - && char.IsLetter(querySearchString[2])) + if (querySearchString.StartsWith(@"\\") + && querySearchString[2] != '\\') return true; // c:\ - if (querySearchString.Length == 3 - && char.IsLetter(querySearchString[0]) + if (char.IsLetter(querySearchString[0]) && querySearchString[1] == ':' && querySearchString[2] == '\\') - return true; - - // c:\\ - if (querySearchString.Length >= 4 - && char.IsLetter(querySearchString[0]) - && querySearchString[1] == ':' - && querySearchString[2] == '\\' - && char.IsLetter(querySearchString[3])) - return true; + { + return querySearchString.Length == 3 || querySearchString[3] != '\\'; + } return false; } From 3effb401b7c0124ea9628e1a2c7d7d6b22343d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 22 Jan 2021 16:22:52 +0800 Subject: [PATCH 49/88] make it become an extension method --- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index a185b2a1c..98beba987 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -148,7 +148,7 @@ namespace Flow.Launcher.Plugin.SharedCommands /// This checks whether a given string is a directory path or network location string. /// It does not check if location actually exists. /// - public static bool IsLocationPathString(string querySearchString) + public static bool IsLocationPathString(this string querySearchString) { if (string.IsNullOrEmpty(querySearchString) || querySearchString.Length < 3) return false; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 6b3a96912..452d16105 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -58,7 +58,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search // Query is a location path with a full environment variable, eg. %appdata%\somefolder\ var isEnvironmentVariablePath = querySearch[1..].Contains("%\\"); - if (!FilesFolders.IsLocationPathString(querySearch) && !isEnvironmentVariablePath) + if (!querySearch.IsLocationPathString() && !isEnvironmentVariablePath) { results.AddRange(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); @@ -70,7 +70,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (isEnvironmentVariablePath) locationPath = EnvironmentVariables.TranslateEnvironmentVariablePath(locationPath); - if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) + if (!FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath).IsLocationPathString()) return results; var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath); From 2db3f829e48331337fcf5c27878cda96282144a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 22 Jan 2021 17:13:35 +0800 Subject: [PATCH 50/88] change logic 1. Disable animation when progressbar is hidden or mainwindow is collapsed 2. resume only when both visibility and progressbar visibility is visible --- Flow.Launcher/MainWindow.xaml.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 96efda94d..77fec72a1 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using System; + +using System; using System.ComponentModel; using System.Windows; using System.Windows.Input; @@ -85,9 +86,14 @@ namespace Flow.Launcher _viewModel.LastQuerySelected = true; } + if (_viewModel.ProgressBarVisibility == Visibility.Visible) + { + _progressBarStoryboard.Resume(); + } } else { + _progressBarStoryboard.Pause(); } } else if (e.PropertyName == nameof(MainViewModel.ProgressBarVisibility)) @@ -98,7 +104,7 @@ namespace Flow.Launcher { _progressBarStoryboard.Pause(); } - else + else if (Visibility == Visibility.Visible) { _progressBarStoryboard.Resume(); } From bc0146e68bda6785bce50f425ce7454068662b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 23 Jan 2021 10:53:01 +0800 Subject: [PATCH 51/88] Use Window Search Orderby instead of getting the result and order them by filename --- .../Search/WindowsIndex/IndexSearch.cs | 9 +++---- .../Search/WindowsIndex/QueryConstructor.cs | 24 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 5b1d47ef8..f162eacbe 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -24,9 +24,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex internal async Task> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token) { - var folderResults = new List(); - var fileResults = new List(); var results = new List(); + var fileResults = new List(); try { @@ -55,7 +54,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex if (dataReaderResults.GetString(2) == "Directory") { - folderResults.Add(resultManager.CreateFolderResult( + results.Add(resultManager.CreateFolderResult( dataReaderResults.GetString(0), path, path, @@ -83,8 +82,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex LogException("General error from performing index search", e); } + results.AddRange(fileResults); + // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. - return results.Concat(folderResults.OrderBy(x => x.Title)).Concat(fileResults.OrderBy(x => x.Title)).ToList(); ; + return results; } internal async Task> WindowsIndexSearchAsync(string searchString, string connectionString, diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index 5718fdb0a..e844801e9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -42,7 +42,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex // Get the ISearchQueryHelper which will help us to translate AQS --> SQL necessary to query the indexer var queryHelper = catalogManager.GetQueryHelper(); - + return queryHelper; } @@ -81,11 +81,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex var previousLevelDirectory = path.Substring(0, indexOfSeparator); if (string.IsNullOrEmpty(itemName)) - return searchDepth + $"{previousLevelDirectory}'"; + return $"{searchDepth}{previousLevelDirectory}'{QueryOrderByFileNameRestriction}"; - return $"(System.FileName LIKE '{itemName}%' " + - $"OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND " + - searchDepth + $"{previousLevelDirectory}'"; + return $"(System.FileName LIKE '{itemName}%' OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND {searchDepth}{previousLevelDirectory}' {QueryOrderByFileNameRestriction}"; } /// @@ -98,7 +96,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex if (path.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > path.LastIndexOf(Constants.DirectorySeperator)) return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path); - return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path); + return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path) + QueryOrderByFileNameRestriction; } /// @@ -107,16 +105,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex public string QueryForAllFilesAndFolders(string userSearchString) { // Generate SQL from constructed parameters, converting the userSearchString from AQS->WHERE clause - return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch(); + return CreateBaseQuery().GenerateSQLFromUserQuery(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch + + QueryOrderByFileNameRestriction; } /// /// Set the required WHERE clause restriction to search for all files and folders. /// - public string QueryWhereRestrictionsForAllFilesAndFoldersSearch() - { - return $"scope='file:'"; - } + public const string QueryWhereRestrictionsForAllFilesAndFoldersSearch = "scope='file:'"; + + public const string QueryOrderByFileNameRestriction = " ORDER BY System.FileName"; + /// /// Search will be performed on all indexed file contents for the specified search keywords. @@ -125,7 +124,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; - return query + QueryWhereRestrictionsForFileContentSearch(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch(); + return query + QueryWhereRestrictionsForFileContentSearch(userSearchString) + " AND " + QueryWhereRestrictionsForAllFilesAndFoldersSearch + + QueryOrderByFileNameRestriction; } /// From 5285c46bc18b1f35d530ef42f75fe16fcc4e6387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 23 Jan 2021 10:59:37 +0800 Subject: [PATCH 52/88] fix testing --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 09c7d9a30..28cc4b3a6 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -116,11 +116,8 @@ namespace Flow.Launcher.Test.Plugins [TestCase("scope='file:'")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString) { - // Given - var queryConstructor = new QueryConstructor(new Settings()); - //When - var resultString = queryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch(); + var resultString = QueryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch; // Then Assert.IsTrue(resultString == expectedString, From 0fe92d35cbe3dd01c73507c69820b671b24d3438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 23 Jan 2021 11:32:41 +0800 Subject: [PATCH 53/88] fix testing and a potential error coding --- Flow.Launcher.Test/Plugins/ExplorerTest.cs | 16 ++++++++-------- .../Search/WindowsIndex/QueryConstructor.cs | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 28cc4b3a6..d0e58c9e0 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -60,8 +60,8 @@ namespace Flow.Launcher.Test.Plugins $"Actual: {result}{Environment.NewLine}"); } - [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\'")] - [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\'")] + [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")] + [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")] public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) { // Given @@ -79,7 +79,7 @@ namespace Flow.Launcher.Test.Plugins [TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " + "FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" + - " AND directory='file:C:\\SomeFolder'")] + " AND directory='file:C:\\SomeFolder' ORDER BY System.FileName")] public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString( string userSearchString, string expectedString) { @@ -127,7 +127,7 @@ namespace Flow.Launcher.Test.Plugins [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:'")] + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString( string userSearchString, string expectedString) { @@ -202,7 +202,7 @@ namespace Flow.Launcher.Test.Plugins } [TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " + - "FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:'")] + "FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")] public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString( string userSearchString, string expectedString) { @@ -292,9 +292,9 @@ namespace Flow.Launcher.Test.Plugins } [TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")] - [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " + - "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " + - "scope='file:c:\\SomeFolder'")] + [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " + + "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " + + "scope='file:c:\\SomeFolder'")] public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString) { // Given diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs index e844801e9..20e85bbb5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/QueryConstructor.cs @@ -81,9 +81,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex var previousLevelDirectory = path.Substring(0, indexOfSeparator); if (string.IsNullOrEmpty(itemName)) - return $"{searchDepth}{previousLevelDirectory}'{QueryOrderByFileNameRestriction}"; + return $"{searchDepth}{previousLevelDirectory}'"; - return $"(System.FileName LIKE '{itemName}%' OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND {searchDepth}{previousLevelDirectory}' {QueryOrderByFileNameRestriction}"; + return $"(System.FileName LIKE '{itemName}%' OR CONTAINS(System.FileName,'\"{itemName}*\"',1033)) AND {searchDepth}{previousLevelDirectory}'"; } /// @@ -94,7 +94,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex string query = "SELECT TOP " + settings.MaxResult + $" {CreateBaseQuery().QuerySelectColumns} FROM {SystemIndex} WHERE "; if (path.LastIndexOf(Constants.AllFilesFolderSearchWildcard) > path.LastIndexOf(Constants.DirectorySeperator)) - return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path); + return query + QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path) + QueryOrderByFileNameRestriction; return query + QueryWhereRestrictionsForTopLevelDirectorySearch(path) + QueryOrderByFileNameRestriction; } From e74a0c99b68160be83c4e897195c8f6daaeae6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 23 Jan 2021 12:05:23 +0800 Subject: [PATCH 54/88] fix most untranslated string in Setting window --- Flow.Launcher/CustomQueryHotkeySetting.xaml | 19 ++++++++++--------- Flow.Launcher/Languages/en.xaml | 3 +++ Flow.Launcher/SettingWindow.xaml | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index 5f4cdff19..bf6a35dff 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -5,7 +5,7 @@ Icon="Images\app.png" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" - Title="Custom Plugin Hotkey" Height="200" Width="674.766"> + Title="{DynamicResource customeQueryHotkeyTitle}" Height="200" Width="674.766"> @@ -19,22 +19,23 @@ - + + - - + + - - + + public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption opt) { - if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult(false, UserSettingSearchPrecision); + if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) + return new MatchResult(false, UserSettingSearchPrecision); query = query.Trim(); - - stringToCompare = _alphabet?.Translate(stringToCompare) ?? stringToCompare; - - // This also can be done by spliting the query - - //(var spaceSplit, var upperSplit) = stringToCompare switch - //{ - // string s when s.Contains(' ') => (s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(w => w.First()), - // default(IEnumerable)), - // string s when s.Any(c => char.IsUpper(c)) && s.Any(c => char.IsLower(c)) => - // (null, Regex.Split(s, @"(? w.First())), - // _ => ((IEnumerable)null, (IEnumerable)null) - //}; + TranslationMapping map; + (stringToCompare, map) = _alphabet?.Translate(stringToCompare) ?? (stringToCompare, null); var currentQueryIndex = 0; var acronymMatchData = new List(); @@ -72,28 +62,24 @@ namespace Flow.Launcher.Infrastructure if (currentQueryIndex >= queryWithoutCase.Length) break; - if (compareIndex == 0 && queryWithoutCase[currentQueryIndex] == char.ToLower(stringToCompare[compareIndex])) - { - acronymMatchData.Add(compareIndex); - currentQueryIndex++; - continue; - } switch (stringToCompare[compareIndex]) { - case char c when compareIndex == 0 && queryWithoutCase[currentQueryIndex] == char.ToLower(stringToCompare[compareIndex]) - || (char.IsUpper(c) && char.ToLower(c) == queryWithoutCase[currentQueryIndex]) - || (char.IsWhiteSpace(c) && char.ToLower(stringToCompare[++compareIndex]) == queryWithoutCase[currentQueryIndex]) - || (char.IsNumber(c) && c == queryWithoutCase[currentQueryIndex]): - acronymMatchData.Add(compareIndex); + case var c when (compareIndex == 0 && queryWithoutCase[currentQueryIndex] == + char.ToLower(stringToCompare[compareIndex])) + || (char.IsUpper(c) && char.ToLower(c) == queryWithoutCase[currentQueryIndex]) + || (char.IsWhiteSpace(c) && char.ToLower(stringToCompare[++compareIndex]) == + queryWithoutCase[currentQueryIndex]) + || (char.IsNumber(c) && c == queryWithoutCase[currentQueryIndex]): + acronymMatchData.Add(map?.MapToOriginalIndex(compareIndex) ?? compareIndex); currentQueryIndex++; continue; - case char c when char.IsWhiteSpace(c): + case var c when char.IsWhiteSpace(c): compareIndex++; acronymScore -= 10; break; - case char c when char.IsUpper(c) || char.IsNumber(c): + case var c when char.IsUpper(c) || char.IsNumber(c): acronymScore -= 10; break; } @@ -105,7 +91,7 @@ namespace Flow.Launcher.Infrastructure var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare; - var querySubstrings = queryWithoutCase.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + var querySubstrings = queryWithoutCase.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); int currentQuerySubstringIndex = 0; var currentQuerySubstring = querySubstrings[currentQuerySubstringIndex]; var currentQuerySubstringCharacterIndex = 0; @@ -120,9 +106,10 @@ namespace Flow.Launcher.Infrastructure var indexList = new List(); List spaceIndices = new List(); - for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++) + for (var compareStringIndex = 0; + compareStringIndex < fullStringToCompareWithoutCase.Length; + compareStringIndex++) { - // To maintain a list of indices which correspond to spaces in the string to compare // To populate the list only for the first query substring if (fullStringToCompareWithoutCase[compareStringIndex].Equals(' ') && currentQuerySubstringIndex == 0) @@ -130,7 +117,8 @@ namespace Flow.Launcher.Infrastructure spaceIndices.Add(compareStringIndex); } - if (fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex]) + if (fullStringToCompareWithoutCase[compareStringIndex] != + currentQuerySubstring[currentQuerySubstringCharacterIndex]) { matchFoundInPreviousLoop = false; continue; @@ -154,14 +142,16 @@ namespace Flow.Launcher.Infrastructure // in order to do so we need to verify all previous chars are part of the pattern var startIndexToVerify = compareStringIndex - currentQuerySubstringCharacterIndex; - if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, fullStringToCompareWithoutCase, currentQuerySubstring)) + if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, + fullStringToCompareWithoutCase, currentQuerySubstring)) { matchFoundInPreviousLoop = true; // if it's the beginning character of the first query substring that is matched then we need to update start index firstMatchIndex = currentQuerySubstringIndex == 0 ? startIndexToVerify : firstMatchIndex; - indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex, firstMatchIndexInWord, indexList); + indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex, + firstMatchIndexInWord, indexList); } } @@ -174,11 +164,13 @@ namespace Flow.Launcher.Infrastructure if (currentQuerySubstringCharacterIndex == currentQuerySubstring.Length) { // if any of the substrings was not matched then consider as all are not matched - allSubstringsContainedInCompareString = matchFoundInPreviousLoop && allSubstringsContainedInCompareString; + allSubstringsContainedInCompareString = + matchFoundInPreviousLoop && allSubstringsContainedInCompareString; currentQuerySubstringIndex++; - allQuerySubstringsMatched = AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length); + allQuerySubstringsMatched = + AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length); if (allQuerySubstringsMatched) break; @@ -188,13 +180,16 @@ namespace Flow.Launcher.Infrastructure } } + // proceed to calculate score if every char or substring without whitespaces matched if (allQuerySubstringsMatched) { var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex); - var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString); + var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, + lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString); - return new MatchResult(true, UserSettingSearchPrecision, indexList, score); + var resultList = indexList.Distinct().Select(x => map?.MapToOriginalIndex(x) ?? x).ToList(); + return new MatchResult(true, UserSettingSearchPrecision, resultList, score); } return new MatchResult(false, UserSettingSearchPrecision); @@ -209,14 +204,15 @@ namespace Flow.Launcher.Infrastructure } else { - int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item)).Where(item => firstMatchIndex > item).FirstOrDefault(); + int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item)) + .FirstOrDefault(item => firstMatchIndex > item); int closestSpaceIndex = ind ?? -1; return closestSpaceIndex; } } private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex, - string fullStringToCompareWithoutCase, string currentQuerySubstring) + string fullStringToCompareWithoutCase, string currentQuerySubstring) { var allMatch = true; for (int indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++) @@ -231,7 +227,8 @@ namespace Flow.Launcher.Infrastructure return allMatch; } - private static List GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex, int firstMatchIndexInWord, List indexList) + private static List GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex, + int firstMatchIndexInWord, List indexList) { var updatedList = new List(); @@ -252,7 +249,8 @@ namespace Flow.Launcher.Infrastructure return currentQuerySubstringIndex >= querySubstringsLength; } - private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, bool allSubstringsContainedInCompareString) + private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, + bool allSubstringsContainedInCompareString) { // A match found near the beginning of a string is scored more than a match found near the end // A match is scored more if the characters in the patterns are closer to each other, @@ -347,7 +345,7 @@ namespace Flow.Launcher.Infrastructure private bool IsSearchPrecisionScoreMet(int rawScore) { - return rawScore >= (int)SearchPrecision; + return rawScore >= (int) SearchPrecision; } private int ScoreAfterSearchPrecisionFilter(int rawScore) @@ -360,4 +358,4 @@ namespace Flow.Launcher.Infrastructure { public bool IgnoreCase { get; set; } = true; } -} +} \ No newline at end of file From 1e016d7aab47f2959ce9d6e3787fa35ea0aca5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 22 Dec 2020 22:58:27 +0800 Subject: [PATCH 76/88] optimize use --- Flow.Launcher.Infrastructure/StringMatcher.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index e885798b7..22334c4bd 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -71,7 +71,7 @@ namespace Flow.Launcher.Infrastructure || (char.IsWhiteSpace(c) && char.ToLower(stringToCompare[++compareIndex]) == queryWithoutCase[currentQueryIndex]) || (char.IsNumber(c) && c == queryWithoutCase[currentQueryIndex]): - acronymMatchData.Add(map?.MapToOriginalIndex(compareIndex) ?? compareIndex); + acronymMatchData.Add(compareIndex); currentQueryIndex++; continue; @@ -86,7 +86,10 @@ namespace Flow.Launcher.Infrastructure } if (acronymMatchData.Count == query.Length && acronymScore >= 60) + { + acronymMatchData = acronymMatchData.Select(x => map?.MapToOriginalIndex(x) ?? x).Distinct().ToList(); return new MatchResult(true, UserSettingSearchPrecision, acronymMatchData, acronymScore); + } var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare; @@ -188,7 +191,7 @@ namespace Flow.Launcher.Infrastructure var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString); - var resultList = indexList.Distinct().Select(x => map?.MapToOriginalIndex(x) ?? x).ToList(); + var resultList = indexList.Select(x => map?.MapToOriginalIndex(x) ?? x).Distinct().ToList(); return new MatchResult(true, UserSettingSearchPrecision, resultList, score); } From 9aa4802542c5e066ed8378fa3f2af6e191336055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 26 Dec 2020 00:29:35 +0800 Subject: [PATCH 77/88] Use Binary Search instead of Linear search to reduce time complexity. Add Key Property for debugging. --- .../PinyinAlphabet.cs | 78 +++++++++++++++---- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs index 4f1aedd4a..be3c58f66 100644 --- a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -18,6 +18,13 @@ namespace Flow.Launcher.Infrastructure private List translatedIndexs = new List(); private int translaedLength = 0; + public string key { get; private set; } + + public void setKey(string key) + { + this.key = key; + } + public void AddNewIndex(int originalIndex, int translatedIndex, int length) { if (constructed) @@ -29,28 +36,64 @@ namespace Flow.Launcher.Infrastructure translaedLength += length - 1; } - public int? MapToOriginalIndex(int translatedIndex) + public int MapToOriginalIndex(int translatedIndex) { if (translatedIndex > translatedIndexs.Last()) return translatedIndex - translaedLength - 1; - - for (var i = 0; i < originalIndexs.Count; i++) - { - if (translatedIndex >= translatedIndexs[i * 2] && translatedIndex < translatedIndexs[i * 2 + 1]) - return originalIndexs[i]; - if (translatedIndex < translatedIndexs[i * 2]) - { - int indexDiff = 0; - for (int j = 0; j < i; j++) - { - indexDiff += translatedIndexs[i * 2 + 1] - translatedIndexs[i * 2] - 1; - } - return translatedIndex - indexDiff; + int lowerBound = 0; + int upperBound = originalIndexs.Count - 1; + + int count = 0; + + + // Corner case handle + if (translatedIndex < translatedIndexs[0]) + return translatedIndex; + if (translatedIndex > translatedIndexs.Last()) + { + int indexDef = 0; + for (int k = 0; k < originalIndexs.Count; k++) + { + indexDef += translatedIndexs[k * 2 + 1] - translatedIndexs[k * 2]; } + + return translatedIndex - indexDef - 1; } - return translatedIndex; + // Binary Search with Range + for (int i = originalIndexs.Count / 2;; count++) + { + if (translatedIndex < translatedIndexs[i * 2]) + { + // move to lower middle + upperBound = i; + i = (i + lowerBound) / 2; + } + else if (translatedIndex > translatedIndexs[i * 2 + 1] - 1) + { + lowerBound = i; + // move to upper middle + // due to floor of integer division, move one up on corner case + i = (i + upperBound + 1) / 2; + } + else + return originalIndexs[i]; + + if (upperBound - lowerBound <= 1 && + translatedIndex > translatedIndexs[lowerBound * 2 + 1] && + translatedIndex < translatedIndexs[upperBound * 2]) + { + int indexDef = 0; + + for (int j = 0; j < upperBound; j++) + { + indexDef += translatedIndexs[j * 2 + 1] - translatedIndexs[j * 2]; + } + + return translatedIndex - indexDef - 1; + } + } } public void endConstruct() @@ -117,7 +160,10 @@ namespace Flow.Launcher.Infrastructure map.endConstruct(); - return _pinyinCache[content] = (resultBuilder.ToString(), map); + var key = resultBuilder.ToString(); + map.setKey(key); + + return _pinyinCache[content] = (key, map); } else { From 213059996af5edcabce739773d903a874ecfb186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 27 Dec 2020 20:16:20 +0800 Subject: [PATCH 78/88] Use inner loop to evaluate acronym match (Big Change) Don't end loop before acronym match end since if acronym match exist, we will use that one. --- Flow.Launcher.Infrastructure/StringMatcher.cs | 99 ++++++++++++------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 22334c4bd..7ade76cdf 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -51,46 +51,13 @@ namespace Flow.Launcher.Infrastructure TranslationMapping map; (stringToCompare, map) = _alphabet?.Translate(stringToCompare) ?? (stringToCompare, null); - var currentQueryIndex = 0; + var currentAcronymQueryIndex = 0; var acronymMatchData = new List(); var queryWithoutCase = opt.IgnoreCase ? query.ToLower() : query; + // preset acronymScore int acronymScore = 100; - for (int compareIndex = 0; compareIndex < stringToCompare.Length; compareIndex++) - { - if (currentQueryIndex >= queryWithoutCase.Length) - break; - - - switch (stringToCompare[compareIndex]) - { - case var c when (compareIndex == 0 && queryWithoutCase[currentQueryIndex] == - char.ToLower(stringToCompare[compareIndex])) - || (char.IsUpper(c) && char.ToLower(c) == queryWithoutCase[currentQueryIndex]) - || (char.IsWhiteSpace(c) && char.ToLower(stringToCompare[++compareIndex]) == - queryWithoutCase[currentQueryIndex]) - || (char.IsNumber(c) && c == queryWithoutCase[currentQueryIndex]): - acronymMatchData.Add(compareIndex); - currentQueryIndex++; - continue; - - case var c when char.IsWhiteSpace(c): - compareIndex++; - acronymScore -= 10; - break; - case var c when char.IsUpper(c) || char.IsNumber(c): - acronymScore -= 10; - break; - } - } - - if (acronymMatchData.Count == query.Length && acronymScore >= 60) - { - acronymMatchData = acronymMatchData.Select(x => map?.MapToOriginalIndex(x) ?? x).Distinct().ToList(); - return new MatchResult(true, UserSettingSearchPrecision, acronymMatchData, acronymScore); - } - var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare; @@ -109,24 +76,72 @@ namespace Flow.Launcher.Infrastructure var indexList = new List(); List spaceIndices = new List(); + bool spaceMet = false; + for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++) { + if (currentAcronymQueryIndex >= queryWithoutCase.Length + || allQuerySubstringsMatched && acronymScore < (int) UserSettingSearchPrecision) + break; + + // To maintain a list of indices which correspond to spaces in the string to compare // To populate the list only for the first query substring - if (fullStringToCompareWithoutCase[compareStringIndex].Equals(' ') && currentQuerySubstringIndex == 0) + if (fullStringToCompareWithoutCase[compareStringIndex] == ' ' && currentQuerySubstringIndex == 0) { spaceIndices.Add(compareStringIndex); } - if (fullStringToCompareWithoutCase[compareStringIndex] != + // Acronym check + if (char.IsUpper(stringToCompare[compareStringIndex]) || + char.IsNumber(stringToCompare[compareStringIndex]) || + char.IsWhiteSpace(stringToCompare[compareStringIndex]) || + spaceMet) + { + if (fullStringToCompareWithoutCase[compareStringIndex] == + queryWithoutCase[currentAcronymQueryIndex]) + { + currentAcronymQueryIndex++; + + if (!spaceMet) + { + char currentCompareChar = stringToCompare[compareStringIndex]; + spaceMet = char.IsWhiteSpace(currentCompareChar); + // if is space, no need to check whether upper or digit, though insignificant + if (!spaceMet && compareStringIndex == 0 || char.IsUpper(currentCompareChar) || + char.IsDigit(currentCompareChar)) + { + acronymMatchData.Add(compareStringIndex); + } + } + else if (!(spaceMet = char.IsWhiteSpace(stringToCompare[compareStringIndex]))) + { + acronymMatchData.Add(compareStringIndex); + } + } + else + { + spaceMet = char.IsWhiteSpace(stringToCompare[compareStringIndex]); + // Acronym Penalty + if (!spaceMet) + { + acronymScore -= 10; + } + } + } + // Acronym end + + if (allQuerySubstringsMatched || fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex]) { matchFoundInPreviousLoop = false; + continue; } + if (firstMatchIndex < 0) { // first matched char will become the start of the compared string @@ -174,8 +189,9 @@ namespace Flow.Launcher.Infrastructure allQuerySubstringsMatched = AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length); + if (allQuerySubstringsMatched) - break; + continue; // otherwise move to the next query substring currentQuerySubstring = querySubstrings[currentQuerySubstringIndex]; @@ -183,6 +199,12 @@ namespace Flow.Launcher.Infrastructure } } + // return acronym Match if possible + if (acronymMatchData.Count == query.Length && acronymScore >= (int) UserSettingSearchPrecision) + { + acronymMatchData = acronymMatchData.Select(x => map?.MapToOriginalIndex(x) ?? x).Distinct().ToList(); + return new MatchResult(true, UserSettingSearchPrecision, acronymMatchData, acronymScore); + } // proceed to calculate score if every char or substring without whitespaces matched if (allQuerySubstringsMatched) @@ -249,6 +271,7 @@ namespace Flow.Launcher.Infrastructure private static bool AllQuerySubstringsMatched(int currentQuerySubstringIndex, int querySubstringsLength) { + // Acronym won't utilize the substring to match return currentQuerySubstringIndex >= querySubstringsLength; } From 1cd21c0ccb36827ca01eb57384b44710d5064561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 25 Jan 2021 06:04:05 +0800 Subject: [PATCH 79/88] Fix testing --- Flow.Launcher.Test/FuzzyMatcherTest.cs | 97 ++++++++++++++++++-------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/Flow.Launcher.Test/FuzzyMatcherTest.cs b/Flow.Launcher.Test/FuzzyMatcherTest.cs index 468b94457..8925ae708 100644 --- a/Flow.Launcher.Test/FuzzyMatcherTest.cs +++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs @@ -40,7 +40,7 @@ namespace Flow.Launcher.Test Enum.GetValues(typeof(StringMatcher.SearchPrecisionScore)) .Cast() .ToList() - .ForEach(x => listToReturn.Add((int)x)); + .ForEach(x => listToReturn.Add((int) x)); return listToReturn; } @@ -92,7 +92,8 @@ namespace Flow.Launcher.Test [TestCase("cand")] [TestCase("cpywa")] [TestCase("ccs")] - public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreaterThanPrecisionScoreResults(string searchTerm) + public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreaterThanPrecisionScoreResults( + string searchTerm) { var results = new List(); var matcher = new StringMatcher(); @@ -108,9 +109,9 @@ namespace Flow.Launcher.Test foreach (var precisionScore in GetPrecisionScores()) { var filteredResult = results.Where(result => result.Score >= precisionScore) - .Select(result => result) - .OrderByDescending(x => x.Score) - .ToList(); + .Select(result => result) + .OrderByDescending(x => x.Score) + .ToList(); Debug.WriteLine(""); Debug.WriteLine("###############################################"); @@ -119,6 +120,7 @@ namespace Flow.Launcher.Test { Debug.WriteLine("SCORE: " + item.Score.ToString() + ", FoundString: " + item.Title); } + Debug.WriteLine("###############################################"); Debug.WriteLine(""); @@ -128,11 +130,11 @@ namespace Flow.Launcher.Test [TestCase(Chrome, Chrome, 157)] [TestCase(Chrome, LastIsChrome, 147)] - [TestCase(Chrome, HelpCureHopeRaiseOnMindEntityChrome, 25)] + [TestCase(Chrome, HelpCureHopeRaiseOnMindEntityChrome, 90)] [TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)] [TestCase(Chrome, CandyCrushSagaFromKing, 0)] - [TestCase("sql", MicrosoftSqlServerManagementStudio, 110)] - [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)]//double spacing intended + [TestCase("sql", MicrosoftSqlServerManagementStudio, 90)] + [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring( string queryString, string compareString, int expectedScore) { @@ -141,20 +143,20 @@ namespace Flow.Launcher.Test var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore; // Should - Assert.AreEqual(expectedScore, rawScore, + Assert.AreEqual(expectedScore, rawScore, $"Expected score for compare string '{compareString}': {expectedScore}, Actual: {rawScore}"); } [TestCase("goo", "Google Chrome", StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("chr", "Google Chrome", StringMatcher.SearchPrecisionScore.Low, true)] [TestCase("chr", "Chrome", StringMatcher.SearchPrecisionScore.Regular, true)] - [TestCase("chr", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("chr", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Low, true)] [TestCase("chr", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("chr", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.None, true)] - [TestCase("ccs", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Low, true)] - [TestCase("cand", "Candy Crush Saga from King",StringMatcher.SearchPrecisionScore.Regular, true)] - [TestCase("cand", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, false)] + [TestCase("ccs", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("cand", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("cand", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, + false)] public void WhenGivenDesiredPrecision_ThenShouldReturn_AllResultsGreaterOrEqual( string queryString, string compareString, @@ -170,7 +172,8 @@ namespace Flow.Launcher.Test Debug.WriteLine(""); Debug.WriteLine("###############################################"); Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}"); - Debug.WriteLine($"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int)expectedPrecisionScore})"); + Debug.WriteLine( + $"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})"); Debug.WriteLine("###############################################"); Debug.WriteLine(""); @@ -179,13 +182,15 @@ namespace Flow.Launcher.Test $"Query:{queryString}{Environment.NewLine} " + $"Compare:{compareString}{Environment.NewLine}" + $"Raw Score: {matchResult.RawScore}{Environment.NewLine}" + - $"Precision Score: {(int)expectedPrecisionScore}"); + $"Precision Score: {(int) expectedPrecisionScore}"); } [TestCase("exce", "OverLeaf-Latex: An online LaTeX editor", StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("term", "Windows Terminal (Preview)", StringMatcher.SearchPrecisionScore.Regular, true)] - [TestCase("sql s managa", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)] - [TestCase("sql' s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)] + [TestCase("sql s managa", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, + false)] + [TestCase("sql' s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, + false)] [TestCase("sql s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("sql manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("sql", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] @@ -195,15 +200,21 @@ namespace Flow.Launcher.Test [TestCase("sql serv man", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("sql studio", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("mic", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("mssms", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("msms", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("chr", "Shutdown", StringMatcher.SearchPrecisionScore.Regular, false)] - [TestCase("mssms", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)] - [TestCase("chr", "Change settings for text-to-speech and for speech recognition (if installed).", StringMatcher.SearchPrecisionScore.Regular, false)] - [TestCase("ch r", "Change settings for text-to-speech and for speech recognition (if installed).", StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("chr", "Change settings for text-to-speech and for speech recognition (if installed).", + StringMatcher.SearchPrecisionScore.Regular, false)] + [TestCase("ch r", "Change settings for text-to-speech and for speech recognition (if installed).", + StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("a test", "This is a test", StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("test", "This is a test", StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("cod", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("code", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("codes", "Visual Studio Codes", StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("vsc", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("vs", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)] + [TestCase("vc", VisualStudioCode, StringMatcher.SearchPrecisionScore.Regular, true)] public void WhenGivenQuery_ShouldReturnResults_ContainingAllQuerySubstrings( string queryString, string compareString, @@ -211,7 +222,7 @@ namespace Flow.Launcher.Test bool expectedPrecisionResult) { // When - var matcher = new StringMatcher { UserSettingSearchPrecision = expectedPrecisionScore }; + var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore}; // Given var matchResult = matcher.FuzzyMatch(queryString, compareString); @@ -219,7 +230,8 @@ namespace Flow.Launcher.Test Debug.WriteLine(""); Debug.WriteLine("###############################################"); Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}"); - Debug.WriteLine($"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int)expectedPrecisionScore})"); + Debug.WriteLine( + $"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int) expectedPrecisionScore})"); Debug.WriteLine("###############################################"); Debug.WriteLine(""); @@ -228,7 +240,7 @@ namespace Flow.Launcher.Test $"Query:{queryString}{Environment.NewLine} " + $"Compare:{compareString}{Environment.NewLine}" + $"Raw Score: {matchResult.RawScore}{Environment.NewLine}" + - $"Precision Score: {(int)expectedPrecisionScore}"); + $"Precision Score: {(int) expectedPrecisionScore}"); } [TestCase("man", "Task Manager", "eManual")] @@ -238,7 +250,7 @@ namespace Flow.Launcher.Test string queryString, string compareString1, string compareString2) { // When - var matcher = new StringMatcher { UserSettingSearchPrecision = StringMatcher.SearchPrecisionScore.Regular }; + var matcher = new StringMatcher {UserSettingSearchPrecision = StringMatcher.SearchPrecisionScore.Regular}; // Given var compareString1Result = matcher.FuzzyMatch(queryString, compareString1); @@ -247,8 +259,10 @@ namespace Flow.Launcher.Test Debug.WriteLine(""); Debug.WriteLine("###############################################"); Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}"); - Debug.WriteLine($"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}"); - Debug.WriteLine($"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); + Debug.WriteLine( + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}"); + Debug.WriteLine( + $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); Debug.WriteLine("###############################################"); Debug.WriteLine(""); @@ -256,13 +270,13 @@ namespace Flow.Launcher.Test Assert.True(compareString1Result.Score > compareString2Result.Score, $"Query: \"{queryString}\"{Environment.NewLine} " + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" + - $"Should be greater than{ Environment.NewLine}" + + $"Should be greater than{Environment.NewLine}" + $"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}"); } [TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")] public void WhenMultipleResults_ExactMatchingResult_ShouldHaveGreatestScore( - string queryString, string firstName, string firstDescription, string firstExecutableName, + string queryString, string firstName, string firstDescription, string firstExecutableName, string secondName, string secondDescription, string secondExecutableName) { // Act @@ -275,15 +289,36 @@ namespace Flow.Launcher.Test var secondDescriptionMatch = matcher.FuzzyMatch(queryString, secondDescription).RawScore; var secondExecutableNameMatch = matcher.FuzzyMatch(queryString, secondExecutableName).RawScore; - var firstScore = new[] { firstNameMatch, firstDescriptionMatch, firstExecutableNameMatch }.Max(); - var secondScore = new[] { secondNameMatch, secondDescriptionMatch, secondExecutableNameMatch }.Max(); + var firstScore = new[] {firstNameMatch, firstDescriptionMatch, firstExecutableNameMatch}.Max(); + var secondScore = new[] {secondNameMatch, secondDescriptionMatch, secondExecutableNameMatch}.Max(); // Assert Assert.IsTrue(firstScore > secondScore, $"Query: \"{queryString}\"{Environment.NewLine} " + $"Name of first: \"{firstName}\", Final Score: {firstScore}{Environment.NewLine}" + - $"Should be greater than{ Environment.NewLine}" + + $"Should be greater than{Environment.NewLine}" + $"Name of second: \"{secondName}\", Final Score: {secondScore}{Environment.NewLine}"); } + + [TestCase("vsc","Visual Studio Code", 100)] + [TestCase("jbr","JetBrain Rider",100)] + [TestCase("jr","JetBrain Rider",90)] + [TestCase("vs","Visual Studio",100)] + [TestCase("vs","Visual Studio Preview",100)] + [TestCase("vsp","Visual Studio Preview",100)] + [TestCase("vsp","Visual Studio",0)] + [TestCase("pc","Postman Canary",100)] + + public void WhenGivenAnAcronymQuery_ShouldReturnAcronymScore(string queryString, string compareString, + int desiredScore) + { + var matcher = new StringMatcher(); + var score = matcher.FuzzyMatch(queryString, compareString).Score; + Assert.IsTrue(score == desiredScore, + $@"Query: ""{queryString}"" + CompareString: ""{compareString}"" + Score: {score} + Desired Score: {desiredScore}"); + } } } \ No newline at end of file From ff5e3695e9e538ceafc997b96adf2a4f7cb0a4f2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 Jan 2021 13:50:41 +1100 Subject: [PATCH 80/88] add return if no quick access links or query --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index fd8e254f8..cac08a6bd 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -45,6 +45,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search && string.IsNullOrEmpty(query.Search)) return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context); + // No records in QuickFolderAccessLinks, user has not typed any query apart from SearchActionKeyword, no need for further search + if (string.IsNullOrEmpty(query.Search)) + return results; + var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks, context); if (quickFolderLinks.Count > 0) From 9914124d200ef5988c0488f0f65a9e4bc3082d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 25 Jan 2021 11:00:56 +0800 Subject: [PATCH 81/88] Remove extra checking --- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index cac08a6bd..d7840af5c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -40,14 +40,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search return await WindowsIndexFileContentSearchAsync(query, querySearch, token).ConfigureAwait(false); // This allows the user to type the assigned action keyword and only see the list of quick folder links - if (settings.QuickFolderAccessLinks.Count > 0 - && query.ActionKeyword == settings.SearchActionKeyword - && string.IsNullOrEmpty(query.Search)) - return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context); - - // No records in QuickFolderAccessLinks, user has not typed any query apart from SearchActionKeyword, no need for further search if (string.IsNullOrEmpty(query.Search)) - return results; + return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context); var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks, context); From 8a56cc6cd8b86e291e158908ec7976784fe8564a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 25 Jan 2021 11:06:03 +0800 Subject: [PATCH 82/88] Use singleton in QuickFolderAccess.cs --- .../Search/FolderLinks/QuickFolderAccess.cs | 26 ++++++++++++------- .../Search/SearchManager.cs | 7 ++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs index 8bd19956e..e9cf7ce80 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs @@ -6,25 +6,31 @@ namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks { public class QuickFolderAccess { - internal List FolderListMatched(Query query, List folderLinks, PluginInitContext context) + private readonly ResultManager _resultManager; + + public QuickFolderAccess(PluginInitContext context) + { + _resultManager = new ResultManager(context); + } + + internal List FolderListMatched(Query query, List folderLinks) { if (string.IsNullOrEmpty(query.Search)) return new List(); string search = query.Search.ToLower(); - - var queriedFolderLinks = folderLinks.Where(x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase)); + + var queriedFolderLinks = + folderLinks.Where(x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase)); return queriedFolderLinks.Select(item => - new ResultManager(context) - .CreateFolderResult(item.Nickname, item.Path, item.Path, query)) - .ToList(); + _resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query)) + .ToList(); } - internal List FolderListAll(Query query, List folderLinks, PluginInitContext context) + internal List FolderListAll(Query query, List folderLinks) => folderLinks - .Select(item => - new ResultManager(context).CreateFolderResult(item.Nickname, item.Path, item.Path, query)) + .Select(item => _resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query)) .ToList(); } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index d7840af5c..14aefeb19 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search private readonly IndexSearch indexSearch; - private readonly QuickFolderAccess quickFolderAccess = new QuickFolderAccess(); + private readonly QuickFolderAccess quickFolderAccess; private readonly ResultManager resultManager; @@ -28,6 +28,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search indexSearch = new IndexSearch(context); resultManager = new ResultManager(context); this.settings = settings; + quickFolderAccess = new QuickFolderAccess(context); } internal async Task> SearchAsync(Query query, CancellationToken token) @@ -41,9 +42,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search // This allows the user to type the assigned action keyword and only see the list of quick folder links if (string.IsNullOrEmpty(query.Search)) - return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks, context); + return quickFolderAccess.FolderListAll(query, settings.QuickFolderAccessLinks); - var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks, context); + var quickFolderLinks = quickFolderAccess.FolderListMatched(query, settings.QuickFolderAccessLinks); if (quickFolderLinks.Count > 0) results.AddRange(quickFolderLinks); From 35782e430884f04b5c84c0458d7a49f856df6532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 25 Jan 2021 11:17:28 +0800 Subject: [PATCH 83/88] Version Bump --- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 76fd36bb5..1e92d2254 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.4.1", + "Version": "1.5.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", From 8dc5def2e921138cfb125cc230cef9f0d91473e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 25 Jan 2021 11:19:59 +0800 Subject: [PATCH 84/88] Remove extra whitespace --- 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 14aefeb19..64fa7b780 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -170,4 +170,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search return indexSearch.PathIsIndexed(pathToDirectory); } } -} +} \ No newline at end of file From e46feb1165ac1f8e717336833b22b8c2f5f3ff4f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 Jan 2021 18:57:58 +1100 Subject: [PATCH 85/88] fix formatting --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 2 -- .../Search/DirectoryInfo/DirectoryInfoSearch.cs | 3 ++- .../Search/FolderLinks/QuickFolderAccess.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 4808e0ee0..4afb9a241 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -280,10 +280,8 @@ namespace Flow.Launcher.ViewModel { Clear(); if (this.Capacity > 8000 && Capacity < this.Capacity) - { this.Capacity = Capacity; - } OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 779827b6d..5124f6fb3 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -81,7 +81,8 @@ namespace Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo } catch (Exception e) { - if (!(e is ArgumentException)) throw e; + if (!(e is ArgumentException)) + throw e; results.Add(new Result {Title = e.Message, Score = 501}); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs index e9cf7ce80..ccaf87ef4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs @@ -33,4 +33,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks .Select(item => _resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query)) .ToList(); } -} \ No newline at end of file +} diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 64fa7b780..14aefeb19 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -170,4 +170,4 @@ namespace Flow.Launcher.Plugin.Explorer.Search return indexSearch.PathIsIndexed(pathToDirectory); } } -} \ No newline at end of file +} From 163bfa303baedc480cb68a1f2ce84a89a3d9d1cf Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 Jan 2021 19:18:13 +1100 Subject: [PATCH 86/88] formatting and naming --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 3 ++- .../Search/FolderLinks/QuickFolderAccess.cs | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 4afb9a241..feab3a751 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -149,10 +149,11 @@ namespace Flow.Launcher.ViewModel public void AddResults(IEnumerable resultsForUpdates, CancellationToken token) { var newResults = NewResults(resultsForUpdates); + if (token.IsCancellationRequested) return; - UpdateResults(newResults, token); + UpdateResults(newResults, token); } private void UpdateResults(List newResults, CancellationToken token = default) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs index ccaf87ef4..6f0020ac9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/FolderLinks/QuickFolderAccess.cs @@ -6,11 +6,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks { public class QuickFolderAccess { - private readonly ResultManager _resultManager; + private readonly ResultManager resultManager; public QuickFolderAccess(PluginInitContext context) { - _resultManager = new ResultManager(context); + resultManager = new ResultManager(context); } internal List FolderListMatched(Query query, List folderLinks) @@ -24,13 +24,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search.FolderLinks folderLinks.Where(x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase)); return queriedFolderLinks.Select(item => - _resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query)) + resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query)) .ToList(); } internal List FolderListAll(Query query, List folderLinks) => folderLinks - .Select(item => _resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query)) + .Select(item => resultManager.CreateFolderResult(item.Nickname, item.Path, item.Path, query)) .ToList(); } } From fd32d4884e689d24ee51d0e02d577985520164a4 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 26 Jan 2021 15:31:21 +1100 Subject: [PATCH 87/88] revert unintended CustomQueryHotkeySetting ui change --- Flow.Launcher/CustomQueryHotkeySetting.xaml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index bf6a35dff..a97f90733 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -19,23 +19,22 @@ - - + - - + + - - + +