From 35a544a095be5df48efd1862b9f0cac300a7c81b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 26 May 2025 20:19:27 +0800 Subject: [PATCH 01/16] Use new model to clear results --- Flow.Launcher/ViewModel/MainViewModel.cs | 16 ++++++++++++++-- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 3 +-- Flow.Launcher/ViewModel/ResultsViewModel.cs | 15 +++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 48652f06358..eaf7202db9d 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1079,6 +1079,8 @@ private bool QueryResultsPreviewed() #region Query + internal bool ShouldClearExistingResults { get; set; } + public void QueryResults() { _ = QueryResultsAsync(false); @@ -1434,11 +1436,16 @@ await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : // Indicate if to clear existing results so to show only ones from plugins with action keywords var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); + if (shouldClearExistingResults) + { + // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update + ShouldClearExistingResults = true; + } _lastQuery = query; _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, - token, reSelect, shouldClearExistingResults))) + token, reSelect))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } @@ -1457,11 +1464,16 @@ void QueryHistoryTask(CancellationToken token) // Indicate if to clear existing results so to show only ones from plugins with action keywords var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); + if (shouldClearExistingResults) + { + // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update + ShouldClearExistingResults = true; + } _lastQuery = query; _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query, - token, reSelect, shouldClearExistingResults))) + token, reSelect))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index 1563f85bae2..bc0be0de81e 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -9,8 +9,7 @@ public record struct ResultsForUpdate( PluginMetadata Metadata, Query Query, CancellationToken Token, - bool ReSelectFirstResult = true, - bool shouldClearExistingResults = false) + bool ReSelectFirstResult = true) { public string ID { get; } = Metadata.ID; } diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 9cd36e5c480..a96312322f1 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -187,11 +187,12 @@ public void AddResults(List newRawResults, string resultId) /// public void AddResults(ICollection resultsForUpdates, CancellationToken token, bool reselect = true) { - var newResults = NewResults(resultsForUpdates); - if (token.IsCancellationRequested) return; + // Since NewResults may need to clear existing results, do not check token cancellation after this point + var newResults = NewResults(resultsForUpdates); + UpdateResults(newResults, reselect, token); } @@ -244,8 +245,10 @@ private List NewResults(ICollection resultsFo var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)); - if (resultsForUpdates.Any(x => x.shouldClearExistingResults)) + // If mainVM has flag to clear existing results, hanlde it here + if (_mainVM != null && _mainVM.ShouldClearExistingResults) { + _mainVM.ShouldClearExistingResults = false; App.API.LogDebug("NewResults", $"Existing results are cleared for query"); return newResults.OrderByDescending(rv => rv.Result.Score).ToList(); } @@ -343,11 +346,14 @@ public void RemoveAll(int Capacity = 512) public void Update(List newItems, CancellationToken token = default) { _token = token; - if (Count == 0 && newItems.Count == 0 || _token.IsCancellationRequested) + + // Since NewResults may need to clear existing results, do not check token cancellation here! + if (Count == 0 && newItems.Count == 0) return; if (editTime < 10 || newItems.Count < 30) { + // RemoveAll will not check token cancellation, so it will clear existing results if (Count != 0) RemoveAll(newItems.Count); AddAll(newItems); editTime++; @@ -355,6 +361,7 @@ public void Update(List newItems, CancellationToken token = def } else { + // Clear will not check token cancellation, so it will clear existing results Clear(); BulkAddAll(newItems); if (Capacity > 8000 && newItems.Count < 3000) From dc346fd82597f56b9897ee1225e8c69c612392f3 Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Mon, 26 May 2025 20:24:59 +0800 Subject: [PATCH 02/16] Fix typos Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- 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 a96312322f1..219a36a679a 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -245,7 +245,7 @@ private List NewResults(ICollection resultsFo var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)); - // If mainVM has flag to clear existing results, hanlde it here + // If mainVM has flag to clear existing results, handle it here if (_mainVM != null && _mainVM.ShouldClearExistingResults) { _mainVM.ShouldClearExistingResults = false; From f8fa9ba485b0e9e95ae8d8373d83800f6617529c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 26 May 2025 20:31:19 +0800 Subject: [PATCH 03/16] Add lock & Improve code quality --- Flow.Launcher/ViewModel/MainViewModel.cs | 29 ++++++++++++++++++--- Flow.Launcher/ViewModel/ResultsViewModel.cs | 3 +-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index eaf7202db9d..2e3ca48729d 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -37,6 +37,9 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable private string _queryTextBeforeLeaveResults; private string _ignoredQueryText; // Used to ignore query text change when switching between context menu and query results + private readonly object _shouldClearExistingResultsLock = new(); + private bool _shouldClearExistingResults; + private readonly FlowLauncherJsonStorage _historyItemsStorage; private readonly FlowLauncherJsonStorage _userSelectedRecordStorage; private readonly FlowLauncherJsonStorageTopMostRecord _topMostRecord; @@ -1079,8 +1082,6 @@ private bool QueryResultsPreviewed() #region Query - internal bool ShouldClearExistingResults { get; set; } - public void QueryResults() { _ = QueryResultsAsync(false); @@ -1439,7 +1440,10 @@ await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : if (shouldClearExistingResults) { // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update - ShouldClearExistingResults = true; + lock (_shouldClearExistingResultsLock) + { + _shouldClearExistingResults = true; + } } _lastQuery = query; _previousIsHomeQuery = currentIsHomeQuery; @@ -1467,7 +1471,10 @@ void QueryHistoryTask(CancellationToken token) if (shouldClearExistingResults) { // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update - ShouldClearExistingResults = true; + lock (_shouldClearExistingResultsLock) + { + _shouldClearExistingResults = true; + } } _lastQuery = query; _previousIsHomeQuery = currentIsHomeQuery; @@ -1708,6 +1715,20 @@ internal bool ResultsSelected(ResultsViewModel results) return selected; } + internal bool CheckShouldClearExistingResultsAndReset() + { + lock (_shouldClearExistingResultsLock) + { + if (_shouldClearExistingResults) + { + _shouldClearExistingResults = false; + return true; + } + + return false; + } + } + #endregion #region Hotkey diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 219a36a679a..b73bfb29d0e 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -246,9 +246,8 @@ private List NewResults(ICollection resultsFo var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)); // If mainVM has flag to clear existing results, handle it here - if (_mainVM != null && _mainVM.ShouldClearExistingResults) + if (_mainVM != null && _mainVM.CheckShouldClearExistingResultsAndReset()) { - _mainVM.ShouldClearExistingResults = false; App.API.LogDebug("NewResults", $"Existing results are cleared for query"); return newResults.OrderByDescending(rv => rv.Result.Score).ToList(); } From e2059475fa90815a4f793832d43bbde133051585 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 26 May 2025 22:43:44 +0800 Subject: [PATCH 04/16] Check token after new item added --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index b73bfb29d0e..fd73cb537b7 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -354,6 +354,11 @@ public void Update(List newItems, CancellationToken token = def { // RemoveAll will not check token cancellation, so it will clear existing results if (Count != 0) RemoveAll(newItems.Count); + + // After results are removed, we need to check the token cancellation + // so that we will not add new items from the cancelled queries + if (token.IsCancellationRequested) return; + AddAll(newItems); editTime++; return; @@ -362,6 +367,11 @@ public void Update(List newItems, CancellationToken token = def { // Clear will not check token cancellation, so it will clear existing results Clear(); + + // After results are removed, we need to check the token cancellation + // so that we will not add new items from the cancelled queries + if (token.IsCancellationRequested) return; + BulkAddAll(newItems); if (Capacity > 8000 && newItems.Count < 3000) { From 8aa0f41d8efad4caeca9610a8878e447b7538108 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 26 May 2025 22:43:55 +0800 Subject: [PATCH 05/16] Improve code comments --- 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 fd73cb537b7..4e8e4604672 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -346,7 +346,7 @@ public void Update(List newItems, CancellationToken token = def { _token = token; - // Since NewResults may need to clear existing results, do not check token cancellation here! + // Since NewResults may need to clear existing results, so we cannot check token cancellation here if (Count == 0 && newItems.Count == 0) return; From 85372c088f09581619db35c778a697f3c23ba649 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 26 May 2025 22:48:28 +0800 Subject: [PATCH 06/16] Use local variable for token --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 4e8e4604672..acde3c3b000 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -295,8 +295,6 @@ public class ResultCollection : List, INotifyCollectionChanged { private long editTime = 0; - private CancellationToken _token; - public event NotifyCollectionChangedEventHandler CollectionChanged; protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) @@ -304,12 +302,12 @@ protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) CollectionChanged?.Invoke(this, e); } - public void BulkAddAll(List resultViews) + private void BulkAddAll(List resultViews, CancellationToken token = default) { AddRange(resultViews); // can return because the list will be cleared next time updated, which include a reset event - if (_token.IsCancellationRequested) + if (token.IsCancellationRequested) return; // manually update event @@ -317,12 +315,12 @@ public void BulkAddAll(List resultViews) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } - private void AddAll(List Items) + private void AddAll(List Items, CancellationToken token = default) { for (int i = 0; i < Items.Count; i++) { var item = Items[i]; - if (_token.IsCancellationRequested) + if (token.IsCancellationRequested) return; Add(item); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i)); @@ -344,35 +342,30 @@ public void RemoveAll(int Capacity = 512) /// public void Update(List newItems, CancellationToken token = default) { - _token = token; - // Since NewResults may need to clear existing results, so we cannot check token cancellation here if (Count == 0 && newItems.Count == 0) return; if (editTime < 10 || newItems.Count < 30) { - // RemoveAll will not check token cancellation, so it will clear existing results if (Count != 0) RemoveAll(newItems.Count); // After results are removed, we need to check the token cancellation // so that we will not add new items from the cancelled queries if (token.IsCancellationRequested) return; - AddAll(newItems); + AddAll(newItems, token); editTime++; - return; } else { - // Clear will not check token cancellation, so it will clear existing results Clear(); // After results are removed, we need to check the token cancellation // so that we will not add new items from the cancelled queries if (token.IsCancellationRequested) return; - BulkAddAll(newItems); + BulkAddAll(newItems, token); if (Capacity > 8000 && newItems.Count < 3000) { Capacity = newItems.Count; From 1ae0dd24136ea02dd5a9b4a7630d9b5e6763389f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 29 May 2025 13:54:13 +0800 Subject: [PATCH 07/16] Fix clear existing results when using IResultUpdate --- Flow.Launcher/ViewModel/MainViewModel.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2e3ca48729d..350fe56929f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -268,6 +268,24 @@ public void RegisterResultsUpdatedEvent() if (token.IsCancellationRequested) return; + App.API.LogDebug(ClassName, $"Update results for plugin <{pair.Metadata.Name}>"); + + // Home query does not support IResultUpdated, so this flag is false + var currentIsHomeQuery = false; + + // Indicate if to clear existing results so to show only ones from plugins with action keywords + var shouldClearExistingResults = ShouldClearExistingResultsForQuery(e.Query, currentIsHomeQuery); + if (shouldClearExistingResults) + { + // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update + lock (_shouldClearExistingResultsLock) + { + _shouldClearExistingResults = true; + } + } + _lastQuery = e.Query; + _previousIsHomeQuery = currentIsHomeQuery; + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, token))) { From b82eea95c9d8d8528fa26a53a2c37591445cffe8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 30 May 2025 11:12:19 +0800 Subject: [PATCH 08/16] Assign last state after all queries are done --- Flow.Launcher/ViewModel/MainViewModel.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 350fe56929f..a28f462d090 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -283,8 +283,6 @@ public void RegisterResultsUpdatedEvent() _shouldClearExistingResults = true; } } - _lastQuery = e.Query; - _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, token))) @@ -1394,6 +1392,10 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b // nothing to do here } + // after all queries are done, we will update the last state + _lastQuery = query; + _previousIsHomeQuery = currentIsHomeQuery; + if (currentCancellationToken.IsCancellationRequested) return; // this should happen once after all queries are done so progress bar should continue @@ -1463,8 +1465,6 @@ await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : _shouldClearExistingResults = true; } } - _lastQuery = query; - _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, token, reSelect))) @@ -1494,8 +1494,6 @@ void QueryHistoryTask(CancellationToken token) _shouldClearExistingResults = true; } } - _lastQuery = query; - _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query, token, reSelect))) From 1206baaeca72eae12116890bfa5bf05ba27613d7 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 30 May 2025 11:36:41 +0800 Subject: [PATCH 09/16] Revert "Assign last state after all queries are done" This reverts commit b82eea95c9d8d8528fa26a53a2c37591445cffe8. --- Flow.Launcher/ViewModel/MainViewModel.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index a28f462d090..350fe56929f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -283,6 +283,8 @@ public void RegisterResultsUpdatedEvent() _shouldClearExistingResults = true; } } + _lastQuery = e.Query; + _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, token))) @@ -1392,10 +1394,6 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b // nothing to do here } - // after all queries are done, we will update the last state - _lastQuery = query; - _previousIsHomeQuery = currentIsHomeQuery; - if (currentCancellationToken.IsCancellationRequested) return; // this should happen once after all queries are done so progress bar should continue @@ -1465,6 +1463,8 @@ await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : _shouldClearExistingResults = true; } } + _lastQuery = query; + _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, token, reSelect))) @@ -1494,6 +1494,8 @@ void QueryHistoryTask(CancellationToken token) _shouldClearExistingResults = true; } } + _lastQuery = query; + _previousIsHomeQuery = currentIsHomeQuery; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query, token, reSelect))) From b999fd0c5395615bb1c2aba48a406faf0cba369c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 30 May 2025 11:56:41 +0800 Subject: [PATCH 10/16] Check should clear existing results after token checked --- Flow.Launcher/ViewModel/MainViewModel.cs | 77 ++++----------------- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 4 +- Flow.Launcher/ViewModel/ResultsViewModel.cs | 6 +- 3 files changed, 19 insertions(+), 68 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 350fe56929f..d852d671a77 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -37,9 +38,6 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable private string _queryTextBeforeLeaveResults; private string _ignoredQueryText; // Used to ignore query text change when switching between context menu and query results - private readonly object _shouldClearExistingResultsLock = new(); - private bool _shouldClearExistingResults; - private readonly FlowLauncherJsonStorage _historyItemsStorage; private readonly FlowLauncherJsonStorage _userSelectedRecordStorage; private readonly FlowLauncherJsonStorageTopMostRecord _topMostRecord; @@ -216,7 +214,17 @@ async Task UpdateActionAsync() while (channelReader.TryRead(out var item)) { if (!item.Token.IsCancellationRequested) + { + // Indicate if to clear existing results so to show only ones from plugins with action keywords + var query = item.Query; + var currentIsHomeQuery = item.IsHomeQuery; + var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); + _lastQuery = item.Query; + _previousIsHomeQuery = currentIsHomeQuery; + item.ShouldClearExistingResults = shouldClearExistingResults; + queue[item.ID] = item; + } } UpdateResultView(queue.Values); @@ -270,24 +278,8 @@ public void RegisterResultsUpdatedEvent() App.API.LogDebug(ClassName, $"Update results for plugin <{pair.Metadata.Name}>"); - // Home query does not support IResultUpdated, so this flag is false - var currentIsHomeQuery = false; - - // Indicate if to clear existing results so to show only ones from plugins with action keywords - var shouldClearExistingResults = ShouldClearExistingResultsForQuery(e.Query, currentIsHomeQuery); - if (shouldClearExistingResults) - { - // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update - lock (_shouldClearExistingResultsLock) - { - _shouldClearExistingResults = true; - } - } - _lastQuery = e.Query; - _previousIsHomeQuery = currentIsHomeQuery; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, - token))) + false, token))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } @@ -1453,21 +1445,8 @@ await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : App.API.LogDebug(ClassName, $"Update results for plugin <{plugin.Metadata.Name}>"); - // Indicate if to clear existing results so to show only ones from plugins with action keywords - var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); - if (shouldClearExistingResults) - { - // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update - lock (_shouldClearExistingResultsLock) - { - _shouldClearExistingResults = true; - } - } - _lastQuery = query; - _previousIsHomeQuery = currentIsHomeQuery; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, - token, reSelect))) + currentIsHomeQuery, token, reSelect))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } @@ -1484,21 +1463,8 @@ void QueryHistoryTask(CancellationToken token) App.API.LogDebug(ClassName, $"Update results for history"); - // Indicate if to clear existing results so to show only ones from plugins with action keywords - var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); - if (shouldClearExistingResults) - { - // Setup the flag to clear existing results so that ResultsViewModel.NewResults will handle in the next update - lock (_shouldClearExistingResultsLock) - { - _shouldClearExistingResults = true; - } - } - _lastQuery = query; - _previousIsHomeQuery = currentIsHomeQuery; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query, - token, reSelect))) + currentIsHomeQuery, token, reSelect))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } @@ -1733,20 +1699,6 @@ internal bool ResultsSelected(ResultsViewModel results) return selected; } - internal bool CheckShouldClearExistingResultsAndReset() - { - lock (_shouldClearExistingResultsLock) - { - if (_shouldClearExistingResults) - { - _shouldClearExistingResults = false; - return true; - } - - return false; - } - } - #endregion #region Hotkey @@ -1912,6 +1864,7 @@ public void UpdateResultView(ICollection resultsForUpdates) { if (!resultsForUpdates.Any()) return; + CancellationToken token; try diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index bc0be0de81e..ca0029bfb3b 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -8,8 +8,10 @@ public record struct ResultsForUpdate( IReadOnlyList Results, PluginMetadata Metadata, Query Query, + bool IsHomeQuery, CancellationToken Token, - bool ReSelectFirstResult = true) + bool ReSelectFirstResult = true, + bool ShouldClearExistingResults = false) { public string ID { get; } = Metadata.ID; } diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index acde3c3b000..af51016574e 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -187,9 +187,6 @@ public void AddResults(List newRawResults, string resultId) /// public void AddResults(ICollection resultsForUpdates, CancellationToken token, bool reselect = true) { - if (token.IsCancellationRequested) - return; - // Since NewResults may need to clear existing results, do not check token cancellation after this point var newResults = NewResults(resultsForUpdates); @@ -245,8 +242,7 @@ private List NewResults(ICollection resultsFo var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)); - // If mainVM has flag to clear existing results, handle it here - if (_mainVM != null && _mainVM.CheckShouldClearExistingResultsAndReset()) + if (resultsForUpdates.Any(x => x.ShouldClearExistingResults)) { App.API.LogDebug("NewResults", $"Existing results are cleared for query"); return newResults.OrderByDescending(rv => rv.Result.Score).ToList(); From 09015f2014792d78fd7c133679d2f76d1fa31c8c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 30 May 2025 12:07:43 +0800 Subject: [PATCH 11/16] Handle isHomeQuery in Query class --- Flow.Launcher.Core/Plugin/QueryBuilder.cs | 6 ++++-- Flow.Launcher.Plugin/Query.cs | 5 +++++ Flow.Launcher/ViewModel/MainViewModel.cs | 11 +++++------ Flow.Launcher/ViewModel/ResultsForUpdate.cs | 1 - 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index fae821736fb..25a32a728d3 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -16,7 +16,8 @@ public static Query Build(string text, Dictionary nonGlobalP Search = string.Empty, RawQuery = string.Empty, SearchTerms = Array.Empty(), - ActionKeyword = string.Empty + ActionKeyword = string.Empty, + IsHomeQuery = true }; } @@ -53,7 +54,8 @@ public static Query Build(string text, Dictionary nonGlobalP Search = search, RawQuery = rawQuery, SearchTerms = searchTerms, - ActionKeyword = actionKeyword + ActionKeyword = actionKeyword, + IsHomeQuery = false }; } } diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index c3eede4c6b6..f50614699fd 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -21,6 +21,11 @@ public class Query /// public bool IsReQuery { get; internal set; } = false; + /// + /// Determines whether the query is a home query. + /// + public bool IsHomeQuery { get; internal init; } = false; + /// /// Search part of a query. /// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as RawQuery. diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index d852d671a77..8eb7420ba10 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -217,7 +216,7 @@ async Task UpdateActionAsync() { // Indicate if to clear existing results so to show only ones from plugins with action keywords var query = item.Query; - var currentIsHomeQuery = item.IsHomeQuery; + var currentIsHomeQuery = query.IsHomeQuery; var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); _lastQuery = item.Query; _previousIsHomeQuery = currentIsHomeQuery; @@ -279,7 +278,7 @@ public void RegisterResultsUpdatedEvent() App.API.LogDebug(ClassName, $"Update results for plugin <{pair.Metadata.Name}>"); if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, - false, token))) + token))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } @@ -1271,7 +1270,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); - var currentIsHomeQuery = query.RawQuery == string.Empty; + var currentIsHomeQuery = query.IsHomeQuery; _updateSource?.Dispose(); @@ -1446,7 +1445,7 @@ await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : App.API.LogDebug(ClassName, $"Update results for plugin <{plugin.Metadata.Name}>"); if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, - currentIsHomeQuery, token, reSelect))) + token, reSelect))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } @@ -1464,7 +1463,7 @@ void QueryHistoryTask(CancellationToken token) App.API.LogDebug(ClassName, $"Update results for history"); if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query, - currentIsHomeQuery, token, reSelect))) + token, reSelect))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index ca0029bfb3b..304dd875716 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -8,7 +8,6 @@ public record struct ResultsForUpdate( IReadOnlyList Results, PluginMetadata Metadata, Query Query, - bool IsHomeQuery, CancellationToken Token, bool ReSelectFirstResult = true, bool ShouldClearExistingResults = false) From 94adfcc59729dece5c0ad894238d510045fa09f1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 31 May 2025 10:54:07 +0800 Subject: [PATCH 12/16] Add more debug info --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index af51016574e..2f4ca6ccf36 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -17,6 +17,8 @@ public class ResultsViewModel : BaseModel { #region Private Fields + private readonly string ClassName = nameof(ResultsViewModel); + public ResultCollection Results { get; } private readonly object _collectionLock = new(); @@ -238,16 +240,20 @@ private List NewResults(List newRawResults, string resu private List NewResults(ICollection resultsForUpdates) { if (!resultsForUpdates.Any()) + { + App.API.LogDebug(ClassName, "No results for updates, returning existing results"); return Results; + } var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)); if (resultsForUpdates.Any(x => x.ShouldClearExistingResults)) { - App.API.LogDebug("NewResults", $"Existing results are cleared for query"); + App.API.LogDebug(ClassName, $"Existing results are cleared for query"); return newResults.OrderByDescending(rv => rv.Result.Score).ToList(); } + App.API.LogDebug(ClassName, $"Keeping existing results for {resultsForUpdates.Count} queries"); return Results.Where(r => r?.Result != null && resultsForUpdates.All(u => u.ID != r.Result.PluginID)) .Concat(newResults) .OrderByDescending(rv => rv.Result.Score) From e0cdb09bb0bf5d752a1b70237e997a6a0e927d96 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 31 May 2025 11:24:15 +0800 Subject: [PATCH 13/16] Fix home query default value --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 8eb7420ba10..0d311ebd10a 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -33,7 +33,7 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable private bool _isQueryRunning; private Query _lastQuery; - private bool _previousIsHomeQuery; + private bool _previousIsHomeQuery = true; // Used to determine if the last query was a home query private string _queryTextBeforeLeaveResults; private string _ignoredQueryText; // Used to ignore query text change when switching between context menu and query results From 12a6b6cb4042b841753b95ca78cc36b4984957ce Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 31 May 2025 11:24:30 +0800 Subject: [PATCH 14/16] Fix async warning in main vm --- 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 bb29d78e5e8..82e0a776ecc 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -101,7 +101,7 @@ public MainWindow() private void ThemeManager_ActualApplicationThemeChanged(ModernWpf.ThemeManager sender, object args) { - _theme.RefreshFrameAsync(); + _ = _theme.RefreshFrameAsync(); } private void OnSourceInitialized(object sender, EventArgs e) From 4bb14fd336488ac0be8aeb4c21540bd80d25d05f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 31 May 2025 11:33:14 +0800 Subject: [PATCH 15/16] Revert home query default value --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 0d311ebd10a..72783c65393 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -33,7 +33,7 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable private bool _isQueryRunning; private Query _lastQuery; - private bool _previousIsHomeQuery = true; // Used to determine if the last query was a home query + private bool _previousIsHomeQuery; private string _queryTextBeforeLeaveResults; private string _ignoredQueryText; // Used to ignore query text change when switching between context menu and query results @@ -223,7 +223,7 @@ async Task UpdateActionAsync() item.ShouldClearExistingResults = shouldClearExistingResults; queue[item.ID] = item; - } + } } UpdateResultView(queue.Values); From 9d972e614fe20a664c91b303277ce0dafb446dba Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 31 May 2025 11:38:59 +0800 Subject: [PATCH 16/16] Pass shouldClearExistingResults flag when overriding results --- Flow.Launcher/ViewModel/MainViewModel.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 72783c65393..802c5c19cd9 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -220,7 +220,16 @@ async Task UpdateActionAsync() var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); _lastQuery = item.Query; _previousIsHomeQuery = currentIsHomeQuery; - item.ShouldClearExistingResults = shouldClearExistingResults; + + // If the queue already has the item, we need to pass the shouldClearExistingResults flag + if (queue.TryGetValue(item.ID, out var existingItem)) + { + item.ShouldClearExistingResults = shouldClearExistingResults || existingItem.ShouldClearExistingResults; + } + else + { + item.ShouldClearExistingResults = shouldClearExistingResults; + } queue[item.ID] = item; }