From 34c3cdabf799734299b835ae42acb71ec5454b21 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 2 May 2025 12:44:17 +0800 Subject: [PATCH 01/32] Improve update logic --- Flow.Launcher.Core/Plugin/QueryBuilder.cs | 12 +- Flow.Launcher.Plugin/Query.cs | 6 + Flow.Launcher.Test/QueryBuilderTest.cs | 6 +- Flow.Launcher/MainWindow.xaml.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 158 ++++++++++++---------- 5 files changed, 105 insertions(+), 79 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index 0ef3f30f5e1..39dea289583 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -6,12 +6,13 @@ namespace Flow.Launcher.Core.Plugin { public static class QueryBuilder { - public static Query Build(string text, Dictionary nonGlobalPlugins) + public static Query Build(string input, string text, Dictionary nonGlobalPlugins) { // replace multiple white spaces with one white space var terms = text.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries); if (terms.Length == 0) - { // nothing was typed + { + // nothing was typed return null; } @@ -21,13 +22,15 @@ public static Query Build(string text, Dictionary nonGlobalP string[] searchTerms; if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled) - { // use non global plugin for query + { + // use non global plugin for query actionKeyword = possibleActionKeyword; search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty; searchTerms = terms[1..]; } else - { // non action keyword + { + // non action keyword actionKeyword = string.Empty; search = rawQuery.TrimStart(); searchTerms = terms; @@ -36,6 +39,7 @@ public static Query Build(string text, Dictionary nonGlobalP return new Query() { Search = search, + Input = input, RawQuery = rawQuery, SearchTerms = searchTerms, ActionKeyword = actionKeyword diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index c3eede4c6b6..86a95a6920b 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -7,6 +7,12 @@ namespace Flow.Launcher.Plugin /// public class Query { + /// + /// Input text in query box. + /// We didn't recommend use this property directly. You should always use Search property. + /// + public string Input { get; internal init; } + /// /// Raw query, this includes action keyword if it has. /// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace. diff --git a/Flow.Launcher.Test/QueryBuilderTest.cs b/Flow.Launcher.Test/QueryBuilderTest.cs index c8ac17748da..3912f26a7d3 100644 --- a/Flow.Launcher.Test/QueryBuilderTest.cs +++ b/Flow.Launcher.Test/QueryBuilderTest.cs @@ -16,7 +16,7 @@ public void ExclusivePluginQueryTest() {">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List {">"}}}} }; - Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins); + Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins); ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.RawQuery); ClassicAssert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword."); @@ -39,7 +39,7 @@ public void ExclusivePluginQueryIgnoreDisabledTest() {">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List {">"}, Disabled = true}}} }; - Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins); + Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins); ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.Search); ClassicAssert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search."); @@ -51,7 +51,7 @@ public void ExclusivePluginQueryIgnoreDisabledTest() [Test] public void GenericPluginQueryTest() { - Query q = QueryBuilder.Build("file.txt file2 file3", new Dictionary()); + Query q = QueryBuilder.Build("file.txt file2 file3", "file.txt file2 file3", new Dictionary()); ClassicAssert.AreEqual("file.txt file2 file3", q.Search); ClassicAssert.AreEqual("", q.ActionKeyword); diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index ae7b098a206..90260df533f 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -408,7 +408,7 @@ private void OnKeyDown(object sender, KeyEventArgs e) && QueryTextBox.CaretIndex == QueryTextBox.Text.Length) { var queryWithoutActionKeyword = - QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search; + QueryBuilder.Build(QueryTextBox.Text, QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search; if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword)) { diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2f1ed0f5103..2fe22d36d9e 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -31,8 +31,9 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable private static readonly string ClassName = nameof(MainViewModel); - private bool _isQueryRunning; private Query _lastQuery; + private Query _runningQuery; // Used for QueryResultAsync + private Query _currentQuery; // Used for ResultsUpdated private string _queryTextBeforeLeaveResults; private readonly FlowLauncherJsonStorage _historyItemsStorage; @@ -235,7 +236,7 @@ public void RegisterResultsUpdatedEvent() var plugin = (IResultUpdated)pair.Plugin; plugin.ResultsUpdated += (s, e) => { - if (e.Query.RawQuery != QueryText || e.Token.IsCancellationRequested) + if (_currentQuery == null || e.Query.RawQuery != _currentQuery.RawQuery || e.Token.IsCancellationRequested) { return; } @@ -255,9 +256,12 @@ public void RegisterResultsUpdatedEvent() PluginManager.UpdatePluginMetadata(resultsCopy, pair.Metadata, e.Query); - if (token.IsCancellationRequested) return; + if (_currentQuery == null || e.Query.RawQuery != _currentQuery.RawQuery || token.IsCancellationRequested) + { + return; + } - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, token))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); @@ -365,7 +369,7 @@ private void LoadContextMenu() [RelayCommand] private void Backspace(object index) { - var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins); + var query = QueryBuilder.Build(QueryText, QueryText.Trim(), PluginManager.NonGlobalPlugins); // GetPreviousExistingDirectory does not require trailing '\', otherwise will return empty string var path = FilesFolders.GetPreviousExistingDirectory((_) => true, query.Search.TrimEnd('\\')); @@ -786,7 +790,7 @@ private ResultsViewModel SelectedResults public Visibility ProgressBarVisibility { get; set; } public Visibility MainWindowVisibility { get; set; } - + // This is to be used for determining the visibility status of the main window instead of MainWindowVisibility // because it is more accurate and reliable representation than using Visibility as a condition check public bool MainWindowVisibilityStatus { get; set; } = true; @@ -1063,7 +1067,7 @@ private bool CanExternalPreviewSelectedResult(out string path) path = QueryResultsPreviewed() ? Results.SelectedItem?.Result?.Preview.FilePath : string.Empty; return !string.IsNullOrEmpty(path); } - + private bool QueryResultsPreviewed() { var previewed = PreviewSelectedItem == Results.SelectedItem; @@ -1196,6 +1200,7 @@ private void QueryHistory() private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { _updateSource?.Cancel(); + _runningQuery = null; var query = await ConstructQueryAsync(QueryText, Settings.CustomShortcuts, Settings.BuiltinShortcuts); @@ -1215,89 +1220,103 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b return; } - _updateSource = new CancellationTokenSource(); + try + { + // Check if the query has changed because query can be changed so fast that + // token of the query between two queries has not been created yet + if (query.Input != QueryText) return; - ProgressBarVisibility = Visibility.Hidden; - _isQueryRunning = true; + _updateSource = new CancellationTokenSource(); - // Switch to ThreadPool thread - await TaskScheduler.Default; + ProgressBarVisibility = Visibility.Hidden; - if (_updateSource.Token.IsCancellationRequested) return; + _runningQuery = query; + _currentQuery = query; - // Update the query's IsReQuery property to true if this is a re-query - query.IsReQuery = isReQuery; + // Switch to ThreadPool thread + await TaskScheduler.Default; - // handle the exclusiveness of plugin using action keyword - RemoveOldQueryResults(query); + if (_updateSource.Token.IsCancellationRequested) return; - _lastQuery = query; + // Update the query's IsReQuery property to true if this is a re-query + query.IsReQuery = isReQuery; - var plugins = PluginManager.ValidPluginsForQuery(query); + // handle the exclusiveness of plugin using action keyword + RemoveOldQueryResults(query); - if (plugins.Count == 1) - { - PluginIconPath = plugins.Single().Metadata.IcoPath; - PluginIconSource = await App.API.LoadImageAsync(PluginIconPath); - SearchIconVisibility = Visibility.Hidden; - } - else - { - PluginIconPath = null; - PluginIconSource = null; - SearchIconVisibility = Visibility.Visible; - } + _lastQuery = query; - // Do not wait for performance improvement - /*if (string.IsNullOrEmpty(query.ActionKeyword)) - { - // Wait 15 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(15, _updateSource.Token); - if (_updateSource.Token.IsCancellationRequested) - return; - }*/ + var plugins = PluginManager.ValidPluginsForQuery(query); - _ = Task.Delay(200, _updateSource.Token).ContinueWith(_ => + if (plugins.Count == 1) + { + PluginIconPath = plugins.Single().Metadata.IcoPath; + PluginIconSource = await App.API.LoadImageAsync(PluginIconPath); + SearchIconVisibility = Visibility.Hidden; + } + else + { + PluginIconPath = null; + PluginIconSource = null; + SearchIconVisibility = Visibility.Visible; + } + + // Do not wait for performance improvement + /*if (string.IsNullOrEmpty(query.ActionKeyword)) + { + // Wait 15 millisecond for query change in global query + // if query changes, return so that it won't be calculated + await Task.Delay(15, _updateSource.Token); + if (_updateSource.Token.IsCancellationRequested) + return; + }*/ + + _ = Task.Delay(200, _updateSource.Token).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 (_isQueryRunning) + if (_runningQuery != null && _runningQuery == query) { ProgressBarVisibility = Visibility.Visible; } }, - _updateSource.Token, - TaskContinuationOptions.NotOnCanceled, - TaskScheduler.Default); + _updateSource.Token, + TaskContinuationOptions.NotOnCanceled, + TaskScheduler.Default); - // plugins are ICollection, meaning LINQ will get the Count and preallocate Array + // plugins are ICollection, meaning LINQ will get the Count and preallocate Array - var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch - { - false => QueryTaskAsync(plugin, _updateSource.Token), - true => Task.CompletedTask - }).ToArray(); + var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch + { + false => QueryTaskAsync(plugin, _updateSource.Token), + true => Task.CompletedTask + }).ToArray(); - try - { - // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first - await Task.WhenAll(tasks); - } - catch (OperationCanceledException) - { - // nothing to do here - } + try + { + // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first + await Task.WhenAll(tasks); + } + catch (OperationCanceledException) + { + // nothing to do here + } - if (_updateSource.Token.IsCancellationRequested) return; + if (_updateSource.Token.IsCancellationRequested) return; - // this should happen once after all queries are done so progress bar should continue - // until the end of all querying - _isQueryRunning = false; + // this should happen once after all queries are done so progress bar should continue + // until the end of all querying + _runningQuery = null; - if (!_updateSource.Token.IsCancellationRequested) + if (!_updateSource.Token.IsCancellationRequested) + { + // update to hidden if this is still the current query + ProgressBarVisibility = Visibility.Hidden; + } + } + finally { - // update to hidden if this is still the current query - ProgressBarVisibility = Visibility.Hidden; + // this make sures running query is null even if the query is canceled + _runningQuery = null; } // Local function @@ -1374,7 +1393,7 @@ private async Task ConstructQueryAsync(string queryText, IEnumerable builtInShortcuts, @@ -1549,9 +1568,6 @@ public bool ShouldIgnoreHotkeys() public void Show() { - // When application is exiting, we should not show the main window - if (App.Exiting) return; - // When application is exiting, the Application.Current will be null Application.Current?.Dispatcher.Invoke(() => { From 381334f5429668da590d12512d3539f7dac158e3 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 2 May 2025 12:46:25 +0800 Subject: [PATCH 02/32] Improve variable names --- Flow.Launcher/ViewModel/MainViewModel.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 2fe22d36d9e..b6203ec508c 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -32,8 +32,8 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable private static readonly string ClassName = nameof(MainViewModel); private Query _lastQuery; - private Query _runningQuery; // Used for QueryResultAsync - private Query _currentQuery; // Used for ResultsUpdated + private Query _progressQuery; // Used for QueryResultAsync + private Query _updateQuery; // Used for ResultsUpdated private string _queryTextBeforeLeaveResults; private readonly FlowLauncherJsonStorage _historyItemsStorage; @@ -236,7 +236,7 @@ public void RegisterResultsUpdatedEvent() var plugin = (IResultUpdated)pair.Plugin; plugin.ResultsUpdated += (s, e) => { - if (_currentQuery == null || e.Query.RawQuery != _currentQuery.RawQuery || e.Token.IsCancellationRequested) + if (_updateQuery == null || e.Query.RawQuery != _updateQuery.RawQuery || e.Token.IsCancellationRequested) { return; } @@ -256,7 +256,7 @@ public void RegisterResultsUpdatedEvent() PluginManager.UpdatePluginMetadata(resultsCopy, pair.Metadata, e.Query); - if (_currentQuery == null || e.Query.RawQuery != _currentQuery.RawQuery || token.IsCancellationRequested) + if (_updateQuery == null || e.Query.RawQuery != _updateQuery.RawQuery || token.IsCancellationRequested) { return; } @@ -1200,7 +1200,7 @@ private void QueryHistory() private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { _updateSource?.Cancel(); - _runningQuery = null; + _progressQuery = null; var query = await ConstructQueryAsync(QueryText, Settings.CustomShortcuts, Settings.BuiltinShortcuts); @@ -1230,8 +1230,8 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b ProgressBarVisibility = Visibility.Hidden; - _runningQuery = query; - _currentQuery = query; + _progressQuery = query; + _updateQuery = query; // Switch to ThreadPool thread await TaskScheduler.Default; @@ -1274,7 +1274,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b _ = Task.Delay(200, _updateSource.Token).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 (_runningQuery != null && _runningQuery == query) + if (_progressQuery != null && _progressQuery == query) { ProgressBarVisibility = Visibility.Visible; } @@ -1305,7 +1305,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b // this should happen once after all queries are done so progress bar should continue // until the end of all querying - _runningQuery = null; + _progressQuery = null; if (!_updateSource.Token.IsCancellationRequested) { @@ -1316,7 +1316,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b finally { // this make sures running query is null even if the query is canceled - _runningQuery = null; + _progressQuery = null; } // Local function From 77b87498768b6c3b30fca95050cf6135936cda5d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 5 May 2025 08:53:09 +0800 Subject: [PATCH 03/32] Revert error change --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 78ab93fa942..3dcd9d207e3 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1604,6 +1604,9 @@ public bool ShouldIgnoreHotkeys() public void Show() { + // When application is exiting, we should not show the main window + if (App.Exiting) return; + // When application is exiting, the Application.Current will be null Application.Current?.Dispatcher.Invoke(() => { From cf3fc6a562fb32c30a252064ccc10e161d3a3400 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 5 May 2025 21:53:39 +0800 Subject: [PATCH 04/32] Fix build issue & Adjust indent --- Flow.Launcher/ViewModel/MainViewModel.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 1d2023cd996..af5bb7daded 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1339,13 +1339,13 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b }*/ _ = Task.Delay(200, _updateSource.Token).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 (_progressQuery != null && _progressQuery == query) - { - ProgressBarVisibility = Visibility.Visible; - } - }, + { + // 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 (_progressQuery != null && _progressQuery == query) + { + ProgressBarVisibility = Visibility.Visible; + } + }, _updateSource.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); @@ -1357,7 +1357,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b { tasks = plugins.Select(plugin => plugin.Metadata.HomeDisabled switch { - false => QueryTaskAsync(plugin, _updateSource.Token), + false => QueryTaskAsync(plugin, true, _updateSource.Token), true => Task.CompletedTask }).ToArray(); @@ -1371,7 +1371,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b { tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch { - false => QueryTaskAsync(plugin, _updateSource.Token), + false => QueryTaskAsync(plugin, false, _updateSource.Token), true => Task.CompletedTask }).ToArray(); } @@ -1405,7 +1405,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } // Local function - async Task QueryTaskAsync(PluginPair plugin, CancellationToken token) + async Task QueryTaskAsync(PluginPair plugin, bool isHomeQuery, CancellationToken token) { App.API.LogDebug(ClassName, $"Wait for querying plugin <{plugin.Metadata.Name}>"); @@ -1482,7 +1482,7 @@ private async Task ConstructQueryAsync(string queryText, IEnumerable Date: Tue, 6 May 2025 11:34:51 +0800 Subject: [PATCH 05/32] Add Input for home query --- Flow.Launcher.Core/Plugin/QueryBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index f2759005471..56136dc423a 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -13,6 +13,7 @@ public static Query Build(string input, string text, Dictionary(), From 8c48cbe0d723430f807e963e46ac8fdd964dd24d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 7 May 2025 19:38:57 +0800 Subject: [PATCH 06/32] Use currentCancellationToken instead --- 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 f55fea95218..9d77eb8ba0f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1292,7 +1292,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b // Switch to ThreadPool thread await TaskScheduler.Default; - if (_updateSource.Token.IsCancellationRequested) return; + if (currentCancellationToken.IsCancellationRequested) return; // Update the query's IsReQuery property to true if this is a re-query query.IsReQuery = isReQuery; From 4c5b5fb7587f703d853c49c70e80fcbb9ffc7c92 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 7 May 2025 19:39:56 +0800 Subject: [PATCH 07/32] Improve code quality --- Flow.Launcher/ViewModel/MainViewModel.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 9d77eb8ba0f..04f4c9575e2 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1267,6 +1267,8 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b return; } + var isHomeQuery = query.RawQuery == string.Empty; + try { // Check if the query has changed because query can be changed so fast that @@ -1275,8 +1277,6 @@ 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 isHomeQuery = query.RawQuery == string.Empty; - _updateSource?.Dispose(); var currentUpdateSource = new CancellationTokenSource(); @@ -1364,7 +1364,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b { tasks = plugins.Select(plugin => plugin.Metadata.HomeDisabled switch { - false => QueryTaskAsync(plugin, true, currentCancellationToken), + false => QueryTaskAsync(plugin, currentCancellationToken), true => Task.CompletedTask }).ToArray(); @@ -1378,7 +1378,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b { tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch { - false => QueryTaskAsync(plugin, false, currentCancellationToken), + false => QueryTaskAsync(plugin, currentCancellationToken), true => Task.CompletedTask }).ToArray(); } @@ -1412,7 +1412,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } // Local function - async Task QueryTaskAsync(PluginPair plugin, bool isHomeQuery, CancellationToken token) + async Task QueryTaskAsync(PluginPair plugin, CancellationToken token) { App.API.LogDebug(ClassName, $"Wait for querying plugin <{plugin.Metadata.Name}>"); From f5993596372dfb810a7d11ec03a93cb08bb5a4d0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 7 May 2025 19:45:45 +0800 Subject: [PATCH 08/32] Improve code quality --- Flow.Launcher.Core/Plugin/QueryBuilder.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index 56136dc423a..9cfba43ea76 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -13,8 +13,8 @@ public static Query Build(string input, string text, Dictionary(), ActionKeyword = string.Empty diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 04f4c9575e2..912d3f57163 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1333,8 +1333,8 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } } - var validPluginNames = plugins.Select(x => $"<{x.Metadata.Name}>"); - App.API.LogDebug(ClassName, $"Valid <{plugins.Count}> plugins: {string.Join(" ", validPluginNames)}"); + App.API.LogDebug(ClassName, $"Valid <{plugins.Count}> plugins: " + + $"{string.Join(" ", plugins.Select(x => $"<{x.Metadata.Name}>"))}"); // Do not wait for performance improvement /*if (string.IsNullOrEmpty(query.ActionKeyword)) From 8450e68f992f13e7909c094adea93084a48ff6bf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 10 May 2025 10:33:23 +0800 Subject: [PATCH 09/32] Remove async --- 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 d2a240e5c23..a7af8e1c484 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -105,7 +105,7 @@ private void OnSourceInitialized(object sender, EventArgs e) Win32Helper.DisableControlBox(this); } - private async void OnLoaded(object sender, RoutedEventArgs _) + private void OnLoaded(object sender, RoutedEventArgs _) { // Check first launch if (_settings.FirstLaunch) From bde74631424d7e1fba5a7ee36915f68ab80b05e0 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sat, 10 May 2025 22:02:24 +0800 Subject: [PATCH 10/32] Fix build issue --- 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 0586654bc1c..1ab48bf9d5e 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1298,7 +1298,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b query.IsReQuery = isReQuery; ICollection plugins = Array.Empty(); - if (isHomeQuery) + if (currentIsHomeQuery) { if (Settings.ShowHomePage) { From 7c12956f31c372603f57cfd20a4691e937857dbc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 19 May 2025 16:45:56 +0800 Subject: [PATCH 11/32] Clear results when there are no update tasks --- Flow.Launcher/ViewModel/MainViewModel.cs | 36 ++++++++++++++---------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index fd01af32e13..060d0fd2c0a 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1245,19 +1245,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b if (query == null) // shortcut expanded { - App.API.LogDebug(ClassName, $"Clear query results"); - - // Hide and clear results again because running query may show and add some results - Results.Visibility = Visibility.Collapsed; - Results.Clear(); - - // Reset plugin icon - PluginIconPath = null; - PluginIconSource = null; - SearchIconVisibility = Visibility.Visible; - - // Hide progress bar again because running query may set this to visible - ProgressBarVisibility = Visibility.Hidden; + ClearResults(); return; } @@ -1351,8 +1339,9 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b { if (ShouldClearExistingResultsForNonQuery(plugins)) { - Results.Clear(); - App.API.LogDebug(ClassName, $"Existing results are cleared for non-query"); + // No update tasks and just return + ClearResults(); + return; } tasks = plugins.Select(plugin => plugin.Metadata.HomeDisabled switch @@ -1405,6 +1394,23 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } // Local function + void ClearResults() + { + App.API.LogDebug(ClassName, $"Clear query results"); + + // Hide and clear results again because running query may show and add some results + Results.Visibility = Visibility.Collapsed; + Results.Clear(); + + // Reset plugin icon + PluginIconPath = null; + PluginIconSource = null; + SearchIconVisibility = Visibility.Visible; + + // Hide progress bar again because running query may set this to visible + ProgressBarVisibility = Visibility.Hidden; + } + async Task QueryTaskAsync(PluginPair plugin, CancellationToken token) { App.API.LogDebug(ClassName, $"Wait for querying plugin <{plugin.Metadata.Name}>"); From 3bf6008d50f3e7384e2fd9c7fa2a9de881d93c19 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 3 Jun 2025 14:34:24 +0800 Subject: [PATCH 12/32] Update code comments --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 371a15dc6ad..18607d7266e 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1278,8 +1278,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b try { - // Check if the query has changed because query can be changed so fast that - // token of the query between two queries has not been created yet + // Check if the input text matches the query text if (query.Input != QueryText) return; App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); From 27ea2e4453483f5a6c5ccaa0c0933d633497b302 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 20 Jul 2025 20:12:17 +0800 Subject: [PATCH 13/32] Improve code quality --- Flow.Launcher/ViewModel/MainViewModel.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ac5d5c69642..c29a9bd494f 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1333,7 +1333,7 @@ private static List GetHistoryItems(IEnumerable historyItem private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { - _updateSource?.Cancel(); + _updateSource?.CancelAsync(); _progressQuery = null; App.API.LogDebug(ClassName, $"Start query with text: <{QueryText}>"); @@ -1425,16 +1425,16 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b }*/ _ = 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 (_progressQuery != null && _progressQuery == query) { - // 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 (_progressQuery != null && _progressQuery == query) - { - ProgressBarVisibility = Visibility.Visible; - } - }, - currentCancellationToken, - TaskContinuationOptions.NotOnCanceled, - TaskScheduler.Default); + ProgressBarVisibility = Visibility.Visible; + } + }, + currentCancellationToken, + TaskContinuationOptions.NotOnCanceled, + TaskScheduler.Default); // plugins are ICollection, meaning LINQ will get the Count and preallocate Array @@ -1927,7 +1927,7 @@ public async Task SetupDialogJumpAsync(nint handle) if (DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) { // Cancel the previous Dialog Jump task - _dialogJumpSource?.Cancel(); + _dialogJumpSource?.CancelAsync(); // Create a new cancellation token source _dialogJumpSource = new CancellationTokenSource(); From 1c0c9e740f39fab18fcf7d705fd1ea570b458650 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 21 Jul 2025 16:17:54 +0800 Subject: [PATCH 14/32] Await cancel async --- 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 c29a9bd494f..5c3560dd987 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1333,7 +1333,7 @@ private static List GetHistoryItems(IEnumerable historyItem private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { - _updateSource?.CancelAsync(); + await _updateSource?.CancelAsync(); _progressQuery = null; App.API.LogDebug(ClassName, $"Start query with text: <{QueryText}>"); @@ -1927,7 +1927,7 @@ public async Task SetupDialogJumpAsync(nint handle) if (DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) { // Cancel the previous Dialog Jump task - _dialogJumpSource?.CancelAsync(); + await _dialogJumpSource?.CancelAsync(); // Create a new cancellation token source _dialogJumpSource = new CancellationTokenSource(); From 5493e5ca07db297ac9e50a4789fab1950e134a01 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 21 Jul 2025 17:54:10 +0800 Subject: [PATCH 15/32] Fix null exception --- Flow.Launcher/ViewModel/MainViewModel.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 5c3560dd987..2b8c90d7402 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1333,7 +1333,10 @@ private static List GetHistoryItems(IEnumerable historyItem private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { - await _updateSource?.CancelAsync(); + if (_updateSource != null) + { + await _updateSource.CancelAsync(); + } _progressQuery = null; App.API.LogDebug(ClassName, $"Start query with text: <{QueryText}>"); @@ -1927,7 +1930,10 @@ public async Task SetupDialogJumpAsync(nint handle) if (DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) { // Cancel the previous Dialog Jump task - await _dialogJumpSource?.CancelAsync(); + if (_dialogJumpSource != null) + { + await _dialogJumpSource.CancelAsync(); + } // Create a new cancellation token source _dialogJumpSource = new CancellationTokenSource(); From b6734b97254ade3522dcb8a90e8b5d3166647059 Mon Sep 17 00:00:00 2001 From: Jack Ye <1160210343@qq.com> Date: Sun, 21 Sep 2025 13:25:04 +0800 Subject: [PATCH 16/32] Fix race: pass the method parameter as Input, not the mutable property. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- 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 168a51c6246..4c1e1ca29e0 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1623,7 +1623,7 @@ private async Task ConstructQueryAsync(string queryText, IEnumerable builtInShortcuts, From f49a86a851105c3116e71b315d84173497a6c76c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 23 Sep 2025 18:23:08 +0800 Subject: [PATCH 17/32] Use Input to check equality & Revert part of logic --- Flow.Launcher/ViewModel/MainViewModel.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 4c1e1ca29e0..de06b39cbae 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -283,7 +283,7 @@ public void RegisterResultsUpdatedEvent() var plugin = (IResultUpdated)pair.Plugin; plugin.ResultsUpdated += (s, e) => { - if (_updateQuery == null || e.Query.RawQuery != _updateQuery.RawQuery || e.Token.IsCancellationRequested) + if (_updateQuery == null || e.Query.Input != _updateQuery.Input || e.Token.IsCancellationRequested) { return; } @@ -311,10 +311,7 @@ public void RegisterResultsUpdatedEvent() PluginManager.UpdatePluginMetadata(resultsCopy, pair.Metadata, e.Query); - if (_updateQuery == null || e.Query.RawQuery != _updateQuery.RawQuery || token.IsCancellationRequested) - { - return; - } + if (token.IsCancellationRequested) return; App.API.LogDebug(ClassName, $"Update results for plugin <{pair.Metadata.Name}>"); @@ -1436,7 +1433,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b _ = 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 (_progressQuery != null && _progressQuery == query) + if (_progressQuery != null && _progressQuery.Input == query.Input) { ProgressBarVisibility = Visibility.Visible; } From 61ee54298fbbd01781c7d93a11f45a97b7db68d1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 29 Sep 2025 13:28:02 +0800 Subject: [PATCH 18/32] Revert CancelAsync to Cancel --- Flow.Launcher/ViewModel/MainViewModel.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 937a29d49bd..ecae4f91cf1 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1334,10 +1334,7 @@ private static List GetHistoryItems(IEnumerable historyItem private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { - if (_updateSource != null) - { - await _updateSource.CancelAsync(); - } + _updateSource?.Cancel(); _progressQuery = null; App.API.LogDebug(ClassName, $"Start query with text: <{QueryText}>"); @@ -1931,10 +1928,7 @@ public async Task SetupDialogJumpAsync(nint handle) if (DialogJump.DialogJumpWindowPosition == DialogJumpWindowPositions.UnderDialog) { // Cancel the previous Dialog Jump task - if (_dialogJumpSource != null) - { - await _dialogJumpSource.CancelAsync(); - } + _dialogJumpSource?.Cancel(); // Create a new cancellation token source _dialogJumpSource = new CancellationTokenSource(); From 5f10e02528dbe1841dbae2403e9f90309cf8dd14 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 29 Sep 2025 13:34:08 +0800 Subject: [PATCH 19/32] No need to set progressQuery --- Flow.Launcher/ViewModel/MainViewModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ecae4f91cf1..61696a1b5ed 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1335,7 +1335,6 @@ private static List GetHistoryItems(IEnumerable historyItem private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true) { _updateSource?.Cancel(); - _progressQuery = null; App.API.LogDebug(ClassName, $"Start query with text: <{QueryText}>"); From 741fecd058c7a53f598981b87a25b1c66e5bd5d8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 29 Sep 2025 13:46:31 +0800 Subject: [PATCH 20/32] Update code comments --- 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 61696a1b5ed..3cce77d9b1c 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1443,7 +1443,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b { if (ShouldClearExistingResultsForNonQuery(plugins)) { - // No update tasks and just return + // there are no update tasks and so we can directly return ClearResults(); return; } @@ -1493,7 +1493,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } finally { - // this make sures running query is null even if the query is canceled + // this make sures progress query is null when this query is canceled _progressQuery = null; } From 6edb742a879cf9ad85a623047beb4b11b14eb1e2 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 29 Sep 2025 13:49:18 +0800 Subject: [PATCH 21/32] Do not check if the input text matches the query text --- Flow.Launcher/ViewModel/MainViewModel.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3cce77d9b1c..9126f529e40 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1356,13 +1356,10 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b return; } + App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); + try { - // Check if the input text matches the query text - if (query.Input != QueryText) return; - - App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); - _updateSource?.Dispose(); var currentUpdateSource = new CancellationTokenSource(); From c2935a4014c9221c5580b8c1f19e8af12320a22b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 29 Sep 2025 13:51:21 +0800 Subject: [PATCH 22/32] Revert change for logging --- 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 9126f529e40..3433ef6617d 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1346,6 +1346,8 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b return; } + App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); + var currentIsHomeQuery = query.IsHomeQuery; var currentIsDialogJump = _isDialogJump; @@ -1356,8 +1358,6 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b return; } - App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); - try { _updateSource?.Dispose(); From a4057cf5622dbc906e39fcc43812b0355a349f08 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 14 Oct 2025 21:58:19 +0800 Subject: [PATCH 23/32] Standardize "work around" to "workaround" in comments Updated comments across multiple files to standardize the phrasing from "work around" to "workaround" for consistency and clarity. Changes were made in `Directory.Build.props`, `Flow.Launcher.csproj`, and `MainWindow.xaml.cs`. These updates are non-functional and only affect documentation within the codebase. --- Directory.Build.props | 2 +- Flow.Launcher/Flow.Launcher.csproj | 2 +- Flow.Launcher/MainWindow.xaml.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a5545af1248..b8c1d13ea91 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ true - + false \ No newline at end of file diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 8c7670426bb..576bf6f2f13 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -185,7 +185,7 @@ - + diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index f9f6a3397c5..c0c54afa960 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -859,7 +859,7 @@ private void InitializeContextMenu() public void UpdatePosition() { - // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 + // Initialize call twice to workaround multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 if (_viewModel.IsDialogJumpWindowUnderDialog()) { InitializeDialogJumpPosition(); @@ -883,7 +883,7 @@ private async Task PositionResetAsync() private void InitializePosition() { - // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 + // Initialize call twice to workaround multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 InitializePositionInner(); InitializePositionInner(); return; From ae76b53f0caaa9a00b21a08cf70d763605f1617f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 15 Oct 2025 13:05:47 +0800 Subject: [PATCH 24/32] Resolve conflicts --- Flow.Launcher/Helper/ResultHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Helper/ResultHelper.cs b/Flow.Launcher/Helper/ResultHelper.cs index 389b06b4f72..f9a341f681c 100644 --- a/Flow.Launcher/Helper/ResultHelper.cs +++ b/Flow.Launcher/Helper/ResultHelper.cs @@ -20,7 +20,7 @@ public static class ResultHelper { var plugin = PluginManager.GetPluginForId(pluginId); if (plugin == null) return null; - var query = QueryBuilder.Build(rawQuery, PluginManager.NonGlobalPlugins); + var query = QueryBuilder.Build(rawQuery, rawQuery, PluginManager.NonGlobalPlugins); if (query == null) return null; try { From 32266c8496672e1c8a347470a6d60dcf21816a4d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 24 Nov 2025 17:39:48 +1100 Subject: [PATCH 25/32] fix typo --- Flow.Launcher.Plugin/Query.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index 74e5ca5c8d8..a0ac9d78942 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -9,14 +9,14 @@ public class Query { /// /// Input text in query box. - /// We didn't recommend use this property directly. You should always use Search property. + /// We don't recommend using this property directly. You should always use Search property. /// public string Input { get; internal init; } /// /// Raw query, this includes action keyword if it has. /// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace. - /// We didn't recommend use this property directly. You should always use Search property. + /// We don't recommend using this property directly. You should always use Search property. /// public string RawQuery { get; internal init; } From 3ebf80df476a3a87607474f051dd6a8f1c3c45ae Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 24 Nov 2025 22:46:56 +1100 Subject: [PATCH 26/32] deprecate RawQuery add OriginalQuery (#4126) --- Flow.Launcher.Core/Plugin/QueryBuilder.cs | 6 +++--- Flow.Launcher.Plugin/Query.cs | 21 +++++++++++++++++---- Flow.Launcher/ViewModel/MainViewModel.cs | 6 +++--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index c316e7cc1c2..0c74078c981 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Flow.Launcher.Plugin; @@ -14,7 +14,7 @@ public static Query Build(string input, string text, Dictionary(), ActionKeyword = string.Empty, @@ -53,7 +53,7 @@ public static Query Build(string input, string text, Dictionary - /// Input text in query box. + /// Original query, exactly how the user has typed into the search box. /// We don't recommend using this property directly. You should always use Search property. /// - public string Input { get; internal init; } + public string OriginalQuery { get; internal init; } /// /// Raw query, this includes action keyword if it has. /// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace. /// We don't recommend using this property directly. You should always use Search property. /// - public string RawQuery { get; internal init; } + [Obsolete("RawQuery is renamed to TrimmedQUery. This property will be removed. Update the code to use TrimmedQuery instead.")] + public string RawQuery { + get { return TrimmedQuery; } + internal init { TrimmedQuery = value; } + } + + /// + /// Original query but with trimmed whitespace. Includes action keyword. + /// It has handled buildin custom query shortkeys and build-in shortcuts. + /// If after exact original query from the search box, should use OriginalQuery property instead. + /// We don't recommend using this property directly. You should always use Search property. + /// + public string TrimmedQuery { get; internal init; } /// /// Determines whether the query was forced to execute again. diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index d2a35c591e7..77ebfe7f5ed 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -284,7 +284,7 @@ public void RegisterResultsUpdatedEvent(PluginPair pair) plugin.ResultsUpdated += (s, e) => { - if (_updateQuery == null || e.Query.Input != _updateQuery.Input || e.Token.IsCancellationRequested) + if (_updateQuery == null || e.Query.OriginalQuery != _updateQuery.OriginalQuery || e.Token.IsCancellationRequested) { return; } @@ -1470,7 +1470,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b _ = 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 (_progressQuery != null && _progressQuery.Input == query.Input) + if (_progressQuery != null && _progressQuery.OriginalQuery == query.OriginalQuery) { ProgressBarVisibility = Visibility.Visible; } From 31c88bfb730fe3ad655177fee3833a46535b5603 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 24 Nov 2025 22:53:45 +1100 Subject: [PATCH 27/32] fix typo --- Flow.Launcher.Plugin/Query.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index 48d3cb4ae82..3431ca46138 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -19,7 +19,7 @@ public class Query /// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace. /// We don't recommend using this property directly. You should always use Search property. /// - [Obsolete("RawQuery is renamed to TrimmedQUery. This property will be removed. Update the code to use TrimmedQuery instead.")] + [Obsolete("RawQuery is renamed to TrimmedQuery. This property will be removed. Update the code to use TrimmedQuery instead.")] public string RawQuery { get { return TrimmedQuery; } internal init { TrimmedQuery = value; } From 21b2756c115bfdf736e873d32d90fa18ca712f1a Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 24 Nov 2025 23:05:04 +1100 Subject: [PATCH 28/32] fixed typos --- Flow.Launcher.Plugin/Query.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index 3431ca46138..29d2bdb4a8c 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json.Serialization; namespace Flow.Launcher.Plugin @@ -16,7 +16,7 @@ public class Query /// /// Raw query, this includes action keyword if it has. - /// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace. + /// It has handled built-in custom query hotkeys and build-in shortcuts, and it trims the whitespace. /// We don't recommend using this property directly. You should always use Search property. /// [Obsolete("RawQuery is renamed to TrimmedQuery. This property will be removed. Update the code to use TrimmedQuery instead.")] @@ -27,8 +27,8 @@ public string RawQuery { /// /// Original query but with trimmed whitespace. Includes action keyword. - /// It has handled buildin custom query shortkeys and build-in shortcuts. - /// If after exact original query from the search box, should use OriginalQuery property instead. + /// It has handled built-in custom query hotkeys and build-in shortcuts. + /// If you need the exact original query from the search box, use OriginalQuery property instead. /// We don't recommend using this property directly. You should always use Search property. /// public string TrimmedQuery { get; internal init; } From 6076ac09ccf224f4b221cd1aea87128aac2a7335 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Nov 2025 20:18:18 +0800 Subject: [PATCH 29/32] Refactor QueryBuilder and deprecate RawQuery property Refactored the `Build` method in `QueryBuilder` to use clearer parameter names (`originalQuery` and `trimmedQuery`) and updated its logic accordingly. Consolidated `using` directives in `QueryBuilder.cs`. Updated the `RawQuery` property in `Query.cs` to use modern getter syntax and marked it as `[Obsolete]`, advising the use of `TrimmedQuery` instead. This prepares for the eventual removal of `RawQuery`. Improved code readability and consistency by aligning variable names and logic with the updated terminology. --- Flow.Launcher.Core/Plugin/QueryBuilder.cs | 12 ++++++------ Flow.Launcher.Plugin/Query.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index 0c74078c981..3e729c436d6 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Flow.Launcher.Plugin; @@ -6,10 +6,10 @@ namespace Flow.Launcher.Core.Plugin { public static class QueryBuilder { - public static Query Build(string input, string text, Dictionary nonGlobalPlugins) + public static Query Build(string originalQuery, string trimmedQuery, Dictionary nonGlobalPlugins) { // home query - if (string.IsNullOrEmpty(text)) + if (string.IsNullOrEmpty(trimmedQuery)) { return new Query() { @@ -23,14 +23,14 @@ public static Query Build(string input, string text, Dictionary [Obsolete("RawQuery is renamed to TrimmedQuery. This property will be removed. Update the code to use TrimmedQuery instead.")] public string RawQuery { - get { return TrimmedQuery; } + get => TrimmedQuery; internal init { TrimmedQuery = value; } } From 23ffe9b2f0a865798504e6e6443542b074f91fd2 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Nov 2025 20:21:10 +0800 Subject: [PATCH 30/32] Refactor to use TrimmedQuery instead of RawQuery Updated the `ToString` method in the `Query` class to return `TrimmedQuery` for consistent handling of query strings. Modified test assertions in `QueryBuilderTest` to validate against `TrimmedQuery` instead of `RawQuery`, ensuring alignment with the updated behavior. Revised the `ChangeQuery` method in `SearchManager` to use `TrimmedQuery`, improving query processing by avoiding unnecessary whitespace or formatting issues. --- Flow.Launcher.Plugin/Query.cs | 2 +- Flow.Launcher.Test/QueryBuilderTest.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index b4fda245e73..bf185211351 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -122,6 +122,6 @@ private string SplitSearch(int index) } /// - public override string ToString() => RawQuery; + public override string ToString() => TrimmedQuery; } } diff --git a/Flow.Launcher.Test/QueryBuilderTest.cs b/Flow.Launcher.Test/QueryBuilderTest.cs index 3912f26a7d3..bade3425423 100644 --- a/Flow.Launcher.Test/QueryBuilderTest.cs +++ b/Flow.Launcher.Test/QueryBuilderTest.cs @@ -18,7 +18,7 @@ public void ExclusivePluginQueryTest() Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins); - ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.RawQuery); + ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.TrimmedQuery); ClassicAssert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword."); ClassicAssert.AreEqual(">", q.ActionKeyword); @@ -42,7 +42,7 @@ public void ExclusivePluginQueryIgnoreDisabledTest() Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins); ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.Search); - ClassicAssert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search."); + ClassicAssert.AreEqual(q.Search, q.TrimmedQuery, "RawQuery should be equal to Search."); ClassicAssert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match."); ClassicAssert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin."); ClassicAssert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters"); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index d7b0690828a..21a6945a8d9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -172,7 +172,7 @@ private List EverythingContentSearchResult(Query query) Action = c => { Settings.EnableEverythingContentSearch = true; - Context.API.ChangeQuery(query.RawQuery, true); + Context.API.ChangeQuery(query.TrimmedQuery, true); return false; } } From c22b4c30ebc762331186a5eda64cc647e67af432 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Nov 2025 20:31:09 +0800 Subject: [PATCH 31/32] Refactor: Replace RawQuery with TrimmedQuery This commit replaces the usage of `RawQuery` with `TrimmedQuery` across the codebase to ensure consistent handling of cleaned-up queries. - Updated `JsonRPCPluginV2.cs` to pass `TrimmedQuery` to `ResultsUpdated`. - Refactored `PluginManager.cs` to use `TrimmedQuery` for `AutoCompleteText`. - Modified `QueryBuilder.cs` to construct queries using `TrimmedQuery`. - Updated `Query.cs` documentation to reflect the use of `TrimmedQuery`. - Adjusted `QueryBuilderTest.cs` assertions to validate `TrimmedQuery`. - Refactored `ResultHelper.cs` to use `TrimmedQuery` in `PopulateResultsAsync`. - Updated `ResultListBox.xaml.cs` to use `TrimmedQuery` for drag-and-drop. - Replaced `RawQuery` with `TrimmedQuery` in `LastOpenedHistoryItem.cs` for comparisons. - Refactored `QueryHistory.cs` to manage history using `TrimmedQuery`. - Updated `TopMostRecord.cs` to use `TrimmedQuery` for top-most record operations. - Refactored `MainViewModel.cs` to use `TrimmedQuery` for `Result` creation and logging. - Added a BOM marker in `MainViewModel.cs` for formatting consistency. This refactor improves reliability by addressing potential whitespace and formatting inconsistencies in user queries. --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 4 +- Flow.Launcher.Core/Plugin/PluginManager.cs | 6 +-- Flow.Launcher.Core/Plugin/QueryBuilder.cs | 9 ++-- Flow.Launcher.Plugin/Query.cs | 2 +- Flow.Launcher.Test/QueryBuilderTest.cs | 2 +- Flow.Launcher/Helper/ResultHelper.cs | 4 +- Flow.Launcher/ResultListBox.xaml.cs | 47 ++++++++++--------- .../Storage/LastOpenedHistoryItem.cs | 4 +- Flow.Launcher/Storage/QueryHistory.cs | 4 +- Flow.Launcher/Storage/TopMostRecord.cs | 22 ++++----- Flow.Launcher/ViewModel/MainViewModel.cs | 8 ++-- 11 files changed, 57 insertions(+), 55 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 148fd969e49..47001914343 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -100,11 +100,11 @@ private void SetupJsonRPC() RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API)); - RPC.AddLocalRpcMethod("UpdateResults", new Action((rawQuery, response) => + RPC.AddLocalRpcMethod("UpdateResults", new Action((trimmedQuery, response) => { var results = ParseResults(response); ResultsUpdated?.Invoke(this, - new ResultUpdatedEventArgs { Query = new Query() { RawQuery = rawQuery }, Results = results }); + new ResultUpdatedEventArgs { Query = new Query() { TrimmedQuery = trimmedQuery }, Results = results }); })); RPC.SynchronizationContext = null; RPC.StartListening(); diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index d3a8003c1cc..54712942c28 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -401,7 +401,7 @@ public static async Task> QueryForPluginAsync(PluginPair pair, Quer { Title = Localize.pluginStillInitializing(metadata.Name), SubTitle = Localize.pluginStillInitializingSubtitle(), - AutoCompleteText = query.RawQuery, + AutoCompleteText = query.TrimmedQuery, IcoPath = metadata.IcoPath, PluginDirectory = metadata.PluginDirectory, ActionKeywordAssigned = query.ActionKeyword, @@ -443,7 +443,7 @@ public static async Task> QueryForPluginAsync(PluginPair pair, Quer { Title = Localize.pluginFailedToRespond(metadata.Name), SubTitle = Localize.pluginFailedToRespondSubtitle(), - AutoCompleteText = query.RawQuery, + AutoCompleteText = query.TrimmedQuery, IcoPath = Constant.ErrorIcon, PluginDirectory = metadata.PluginDirectory, ActionKeywordAssigned = query.ActionKeyword, @@ -467,7 +467,7 @@ public static async Task> QueryHomeForPluginAsync(PluginPair pair, { Title = Localize.pluginStillInitializing(metadata.Name), SubTitle = Localize.pluginStillInitializingSubtitle(), - AutoCompleteText = query.RawQuery, + AutoCompleteText = query.TrimmedQuery, IcoPath = metadata.IcoPath, PluginDirectory = metadata.PluginDirectory, ActionKeywordAssigned = query.ActionKeyword, diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index 3e729c436d6..aac620cce64 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -15,7 +15,7 @@ public static Query Build(string originalQuery, string trimmedQuery, Dictionary< { Search = string.Empty, OriginalQuery = string.Empty, - RawQuery = string.Empty, + TrimmedQuery = string.Empty, SearchTerms = Array.Empty(), ActionKeyword = string.Empty, IsHomeQuery = true @@ -30,7 +30,6 @@ public static Query Build(string originalQuery, string trimmedQuery, Dictionary< return null; } - var rawQuery = trimmedQuery; string actionKeyword, search; string possibleActionKeyword = terms[0]; string[] searchTerms; @@ -39,14 +38,14 @@ public static Query Build(string originalQuery, string trimmedQuery, Dictionary< { // use non global plugin for query actionKeyword = possibleActionKeyword; - search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty; + search = terms.Length > 1 ? trimmedQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty; searchTerms = terms[1..]; } else { // non action keyword actionKeyword = string.Empty; - search = rawQuery.TrimStart(); + search = trimmedQuery.TrimStart(); searchTerms = terms; } @@ -54,7 +53,7 @@ public static Query Build(string originalQuery, string trimmedQuery, Dictionary< { Search = search, OriginalQuery = originalQuery, - RawQuery = rawQuery, + TrimmedQuery = trimmedQuery, SearchTerms = searchTerms, ActionKeyword = actionKeyword, IsHomeQuery = false diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index bf185211351..13ffcb096e9 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -47,7 +47,7 @@ public string RawQuery { /// /// Search part of a query. - /// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as RawQuery. + /// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as TrimmedQuery. /// Since we allow user to switch a exclusive plugin to generic plugin, /// so this property will always give you the "real" query part of the query /// diff --git a/Flow.Launcher.Test/QueryBuilderTest.cs b/Flow.Launcher.Test/QueryBuilderTest.cs index bade3425423..0ede781f8ab 100644 --- a/Flow.Launcher.Test/QueryBuilderTest.cs +++ b/Flow.Launcher.Test/QueryBuilderTest.cs @@ -42,7 +42,7 @@ public void ExclusivePluginQueryIgnoreDisabledTest() Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins); ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.Search); - ClassicAssert.AreEqual(q.Search, q.TrimmedQuery, "RawQuery should be equal to Search."); + ClassicAssert.AreEqual(q.Search, q.TrimmedQuery, "TrimmedQuery should be equal to Search."); ClassicAssert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match."); ClassicAssert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin."); ClassicAssert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters"); diff --git a/Flow.Launcher/Helper/ResultHelper.cs b/Flow.Launcher/Helper/ResultHelper.cs index 1943ae90504..b8b7ff98ec5 100644 --- a/Flow.Launcher/Helper/ResultHelper.cs +++ b/Flow.Launcher/Helper/ResultHelper.cs @@ -16,11 +16,11 @@ public static class ResultHelper return await PopulateResultsAsync(item.PluginID, item.Query, item.Title, item.SubTitle, item.RecordKey); } - public static async Task PopulateResultsAsync(string pluginId, string rawQuery, string title, string subTitle, string recordKey) + public static async Task PopulateResultsAsync(string pluginId, string trimmedQuery, string title, string subTitle, string recordKey) { var plugin = PluginManager.GetPluginForId(pluginId); if (plugin == null) return null; - var query = QueryBuilder.Build(rawQuery, rawQuery, PluginManager.GetNonGlobalPlugins()); + var query = QueryBuilder.Build(trimmedQuery, trimmedQuery, PluginManager.GetNonGlobalPlugins()); if (query == null) return null; try { diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index ac51b195c7b..fcc73e9ce1b 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -9,7 +10,7 @@ namespace Flow.Launcher { public partial class ResultListBox { - protected object _lock = new object(); + protected Lock _lock = new(); private Point _lastpos; private ListBoxItem curItem = null; public ResultListBox() @@ -88,12 +89,11 @@ private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) } } - - private Point start; - private string path; - private string query; + private Point _start; + private string _path; + private string _trimmedQuery; // this method is called by the UI thread, which is single threaded, so we can be sloppy with locking - private bool isDragging; + private bool _isDragging; private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { @@ -104,53 +104,55 @@ private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEve Result: { CopyText: { } copyText, - OriginQuery.RawQuery: { } rawQuery + OriginQuery.TrimmedQuery: { } trimmedQuery } } }) return; - path = copyText; - query = rawQuery; - start = e.GetPosition(null); - isDragging = true; + _path = copyText; + _trimmedQuery = trimmedQuery; + _start = e.GetPosition(null); + _isDragging = true; } + private void ResultList_MouseMove(object sender, MouseEventArgs e) { - if (e.LeftButton != MouseButtonState.Pressed || !isDragging) + if (e.LeftButton != MouseButtonState.Pressed || !_isDragging) { - start = default; - path = string.Empty; - query = string.Empty; - isDragging = false; + _start = default; + _path = string.Empty; + _trimmedQuery = string.Empty; + _isDragging = false; return; } - if (!File.Exists(path) && !Directory.Exists(path)) + if (!File.Exists(_path) && !Directory.Exists(_path)) return; Point mousePosition = e.GetPosition(null); - Vector diff = this.start - mousePosition; + Vector diff = _start - mousePosition; if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance || Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance) return; - isDragging = false; + _isDragging = false; App.API.HideMainWindow(); var data = new DataObject(DataFormats.FileDrop, new[] { - path + _path }); // Reassigning query to a new variable because for some reason // after DragDrop.DoDragDrop call, 'query' loses its content, i.e. becomes empty string - var rawQuery = query; + var trimmedQuery = _trimmedQuery; var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy); if (effect == DragDropEffects.Move) - App.API.ChangeQuery(rawQuery, true); + App.API.ChangeQuery(trimmedQuery, true); } + private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result }) @@ -158,6 +160,7 @@ private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseBut RightClickResultCommand?.Execute(result.Result); } + private void ResultListBox_OnPreviewMouseUp(object sender, MouseButtonEventArgs e) { if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result }) diff --git a/Flow.Launcher/Storage/LastOpenedHistoryItem.cs b/Flow.Launcher/Storage/LastOpenedHistoryItem.cs index 47647066c91..7f375e34d35 100644 --- a/Flow.Launcher/Storage/LastOpenedHistoryItem.cs +++ b/Flow.Launcher/Storage/LastOpenedHistoryItem.cs @@ -19,13 +19,13 @@ public bool Equals(Result r) return Title == r.Title && SubTitle == r.SubTitle && PluginID == r.PluginID - && Query == r.OriginQuery.RawQuery; + && Query == r.OriginQuery.TrimmedQuery; } else { return RecordKey == r.RecordKey && PluginID == r.PluginID - && Query == r.OriginQuery.RawQuery; + && Query == r.OriginQuery.TrimmedQuery; } } } diff --git a/Flow.Launcher/Storage/QueryHistory.cs b/Flow.Launcher/Storage/QueryHistory.cs index 8284f7eea01..6998a4ae3f0 100644 --- a/Flow.Launcher/Storage/QueryHistory.cs +++ b/Flow.Launcher/Storage/QueryHistory.cs @@ -35,7 +35,7 @@ public void PopulateHistoryFromLegacyHistory() public void Add(Result result) { - if (string.IsNullOrEmpty(result.OriginQuery.RawQuery)) return; + if (string.IsNullOrEmpty(result.OriginQuery.TrimmedQuery)) return; if (string.IsNullOrEmpty(result.PluginID)) return; // Maintain the max history limit @@ -57,7 +57,7 @@ public void Add(Result result) Title = result.Title, SubTitle = result.SubTitle, PluginID = result.PluginID, - Query = result.OriginQuery.RawQuery, + Query = result.OriginQuery.TrimmedQuery, RecordKey = result.RecordKey, ExecutedDateTime = DateTime.Now }); diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index 7fc9dcaaa48..9708eab974b 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -113,7 +113,7 @@ internal class TopMostRecord internal bool IsTopMost(Result result) { - if (records.IsEmpty || !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + if (records.IsEmpty || !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return false; } @@ -124,7 +124,7 @@ internal bool IsTopMost(Result result) internal void Remove(Result result) { - records.Remove(result.OriginQuery.RawQuery, out _); + records.Remove(result.OriginQuery.TrimmedQuery, out _); } internal void AddOrUpdate(Result result) @@ -136,7 +136,7 @@ internal void AddOrUpdate(Result result) SubTitle = result.SubTitle, RecordKey = result.RecordKey }; - records.AddOrUpdate(result.OriginQuery.RawQuery, record, (key, oldValue) => record); + records.AddOrUpdate(result.OriginQuery.TrimmedQuery, record, (key, oldValue) => record); } } @@ -154,7 +154,7 @@ internal bool IsTopMost(Result result) // origin query is null when user select the context menu item directly of one item from query list // in this case, we do not need to check if the result is top most if (records.IsEmpty || result.OriginQuery == null || - !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return false; } @@ -168,7 +168,7 @@ internal int GetTopMostIndex(Result result) // origin query is null when user select the context menu item directly of one item from query list // in this case, we do not need to check if the result is top most if (records.IsEmpty || result.OriginQuery == null || - !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return -1; } @@ -194,7 +194,7 @@ internal void Remove(Result result) // origin query is null when user select the context menu item directly of one item from query list // in this case, we do not need to remove the record if (result.OriginQuery == null || - !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + !records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { return; } @@ -204,12 +204,12 @@ internal void Remove(Result result) if (queue.IsEmpty) { // if the queue is empty, remove the queue from the dictionary - records.TryRemove(result.OriginQuery.RawQuery, out _); + records.TryRemove(result.OriginQuery.TrimmedQuery, out _); } else { // change the queue in the dictionary - records[result.OriginQuery.RawQuery] = queue; + records[result.OriginQuery.TrimmedQuery] = queue; } } @@ -229,19 +229,19 @@ internal void AddOrUpdate(Result result) SubTitle = result.SubTitle, RecordKey = result.RecordKey }; - if (!records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + if (!records.TryGetValue(result.OriginQuery.TrimmedQuery, out var value)) { // create a new queue if it does not exist value = new ConcurrentQueue(); value.Enqueue(record); - records.TryAdd(result.OriginQuery.RawQuery, value); + records.TryAdd(result.OriginQuery.TrimmedQuery, value); } else { // add or update the record in the queue var queue = new ConcurrentQueue(value.Where(r => !r.Equals(result))); // make sure we don't have duplicates queue.Enqueue(record); - records[result.OriginQuery.RawQuery] = queue; + records[result.OriginQuery.TrimmedQuery] = queue; } } } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 77ebfe7f5ed..c35f96e6200 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -1328,7 +1328,7 @@ private List GetHistoryItems(IEnumerable historyI Title = Localize.executeQuery(h.Query), SubTitle = Localize.lastExecuteTime(h.ExecutedDateTime), IcoPath = Constant.HistoryIcon, - OriginQuery = new Query { RawQuery = h.Query }, + OriginQuery = new Query { TrimmedQuery = h.Query }, Action = _ => { App.API.BackToQueryResults(); @@ -1351,7 +1351,7 @@ private List GetHistoryItems(IEnumerable historyI h.Title, SubTitle = Localize.lastExecuteTime(h.ExecutedDateTime), IcoPath = Constant.HistoryIcon, - OriginQuery = new Query { RawQuery = h.Query }, + OriginQuery = new Query { TrimmedQuery = h.Query }, AsyncAction = async c => { var reflectResult = await ResultHelper.PopulateResultsAsync(h); @@ -1392,7 +1392,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b return; } - App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and RawQuery <{query.RawQuery}>"); + App.API.LogDebug(ClassName, $"Start query with ActionKeyword <{query.ActionKeyword}> and TrimmedQuery <{query.TrimmedQuery}>"); var currentIsHomeQuery = query.IsHomeQuery; var currentIsDialogJump = _isDialogJump; From 2046c0d5e602b4d150d1c8ed50219bf5e95371e8 Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Tue, 25 Nov 2025 19:04:01 +0800 Subject: [PATCH 32/32] Fix typos Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Flow.Launcher.Plugin/Query.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index 13ffcb096e9..79e6f7d62d6 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -16,7 +16,7 @@ public class Query /// /// Raw query, this includes action keyword if it has. - /// It has handled built-in custom query hotkeys and build-in shortcuts, and it trims the whitespace. + /// It has handled built-in custom query hotkeys and built-in shortcuts, and it trims the whitespace. /// We don't recommend using this property directly. You should always use Search property. /// [Obsolete("RawQuery is renamed to TrimmedQuery. This property will be removed. Update the code to use TrimmedQuery instead.")]