Skip to content

Commit 17d9218

Browse files
Jack251970TBM13
authored andcommitted
Improve and fix query result update logic issue & provide access to exact query typed by user (Flow-Launcher#3502)
1 parent c9555e1 commit 17d9218

File tree

11 files changed

+186
-147
lines changed

11 files changed

+186
-147
lines changed

Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ private void SetupJsonRPC()
100100

101101
RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API));
102102

103-
RPC.AddLocalRpcMethod("UpdateResults", new Action<string, JsonRPCQueryResponseModel>((rawQuery, response) =>
103+
RPC.AddLocalRpcMethod("UpdateResults", new Action<string, JsonRPCQueryResponseModel>((trimmedQuery, response) =>
104104
{
105105
var results = ParseResults(response);
106106
ResultsUpdated?.Invoke(this,
107-
new ResultUpdatedEventArgs { Query = new Query() { RawQuery = rawQuery }, Results = results });
107+
new ResultUpdatedEventArgs { Query = new Query() { TrimmedQuery = trimmedQuery }, Results = results });
108108
}));
109109
RPC.SynchronizationContext = null;
110110
RPC.StartListening();

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Quer
365365
{
366366
Title = Localize.pluginStillInitializing(metadata.Name),
367367
SubTitle = Localize.pluginStillInitializingSubtitle(),
368-
AutoCompleteText = query.RawQuery,
368+
AutoCompleteText = query.TrimmedQuery,
369369
IcoPath = metadata.IcoPath,
370370
PluginDirectory = metadata.PluginDirectory,
371371
ActionKeywordAssigned = query.ActionKeyword,
@@ -407,7 +407,7 @@ public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Quer
407407
{
408408
Title = Localize.pluginFailedToRespond(metadata.Name),
409409
SubTitle = Localize.pluginFailedToRespondSubtitle(),
410-
AutoCompleteText = query.RawQuery,
410+
AutoCompleteText = query.TrimmedQuery,
411411
IcoPath = Constant.ErrorIcon,
412412
PluginDirectory = metadata.PluginDirectory,
413413
ActionKeywordAssigned = query.ActionKeyword,
@@ -431,7 +431,7 @@ public static async Task<List<Result>> QueryHomeForPluginAsync(PluginPair pair,
431431
{
432432
Title = Localize.pluginStillInitializing(metadata.Name),
433433
SubTitle = Localize.pluginStillInitializingSubtitle(),
434-
AutoCompleteText = query.RawQuery,
434+
AutoCompleteText = query.TrimmedQuery,
435435
IcoPath = metadata.IcoPath,
436436
PluginDirectory = metadata.PluginDirectory,
437437
ActionKeywordAssigned = query.ActionKeyword,

Flow.Launcher.Core/Plugin/QueryBuilder.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,30 @@ namespace Flow.Launcher.Core.Plugin
66
{
77
public static class QueryBuilder
88
{
9-
public static Query Build(string text, Dictionary<string, PluginPair> nonGlobalPlugins)
9+
public static Query Build(string originalQuery, string trimmedQuery, Dictionary<string, PluginPair> nonGlobalPlugins)
1010
{
1111
// home query
12-
if (string.IsNullOrEmpty(text))
12+
if (string.IsNullOrEmpty(trimmedQuery))
1313
{
1414
return new Query()
1515
{
1616
Search = string.Empty,
17-
RawQuery = string.Empty,
17+
OriginalQuery = string.Empty,
18+
TrimmedQuery = string.Empty,
1819
SearchTerms = Array.Empty<string>(),
1920
ActionKeyword = string.Empty,
2021
IsHomeQuery = true
2122
};
2223
}
2324

2425
// replace multiple white spaces with one white space
25-
var terms = text.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries);
26+
var terms = trimmedQuery.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries);
2627
if (terms.Length == 0)
2728
{
2829
// nothing was typed
2930
return null;
3031
}
3132

32-
var rawQuery = text;
3333
string actionKeyword, search;
3434
string possibleActionKeyword = terms[0];
3535
string[] searchTerms;
@@ -38,21 +38,22 @@ public static Query Build(string text, Dictionary<string, PluginPair> nonGlobalP
3838
{
3939
// use non global plugin for query
4040
actionKeyword = possibleActionKeyword;
41-
search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty;
41+
search = terms.Length > 1 ? trimmedQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty;
4242
searchTerms = terms[1..];
4343
}
4444
else
4545
{
4646
// non action keyword
4747
actionKeyword = string.Empty;
48-
search = rawQuery.TrimStart();
48+
search = trimmedQuery.TrimStart();
4949
searchTerms = terms;
5050
}
5151

5252
return new Query()
5353
{
5454
Search = search,
55-
RawQuery = rawQuery,
55+
OriginalQuery = originalQuery,
56+
TrimmedQuery = trimmedQuery,
5657
SearchTerms = searchTerms,
5758
ActionKeyword = actionKeyword,
5859
IsHomeQuery = false

Flow.Launcher.Plugin/Query.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Text.Json.Serialization;
1+
using System;
2+
using System.Text.Json.Serialization;
23

34
namespace Flow.Launcher.Plugin
45
{
@@ -7,12 +8,30 @@ namespace Flow.Launcher.Plugin
78
/// </summary>
89
public class Query
910
{
11+
/// <summary>
12+
/// Original query, exactly how the user has typed into the search box.
13+
/// We don't recommend using this property directly. You should always use Search property.
14+
/// </summary>
15+
public string OriginalQuery { get; internal init; }
16+
1017
/// <summary>
1118
/// Raw query, this includes action keyword if it has.
12-
/// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace.
13-
/// We didn't recommend use this property directly. You should always use Search property.
19+
/// It has handled built-in custom query hotkeys and built-in shortcuts, and it trims the whitespace.
20+
/// We don't recommend using this property directly. You should always use Search property.
21+
/// </summary>
22+
[Obsolete("RawQuery is renamed to TrimmedQuery. This property will be removed. Update the code to use TrimmedQuery instead.")]
23+
public string RawQuery {
24+
get => TrimmedQuery;
25+
internal init { TrimmedQuery = value; }
26+
}
27+
28+
/// <summary>
29+
/// Original query but with trimmed whitespace. Includes action keyword.
30+
/// It has handled built-in custom query hotkeys and build-in shortcuts.
31+
/// If you need the exact original query from the search box, use OriginalQuery property instead.
32+
/// We don't recommend using this property directly. You should always use Search property.
1433
/// </summary>
15-
public string RawQuery { get; internal init; }
34+
public string TrimmedQuery { get; internal init; }
1635

1736
/// <summary>
1837
/// Determines whether the query was forced to execute again.
@@ -28,7 +47,7 @@ public class Query
2847

2948
/// <summary>
3049
/// Search part of a query.
31-
/// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as RawQuery.
50+
/// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as TrimmedQuery.
3251
/// Since we allow user to switch a exclusive plugin to generic plugin,
3352
/// so this property will always give you the "real" query part of the query
3453
/// </summary>
@@ -103,6 +122,6 @@ private string SplitSearch(int index)
103122
}
104123

105124
/// <inheritdoc />
106-
public override string ToString() => RawQuery;
125+
public override string ToString() => TrimmedQuery;
107126
}
108127
}

Flow.Launcher.Test/QueryBuilderTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public void ExclusivePluginQueryTest()
1616
{">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List<string> {">"}}}}
1717
};
1818

19-
Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins);
19+
Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins);
2020

21-
ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.RawQuery);
21+
ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.TrimmedQuery);
2222
ClassicAssert.AreEqual("ping google.com -n 20 -6", q.Search, "Search should not start with the ActionKeyword.");
2323
ClassicAssert.AreEqual(">", q.ActionKeyword);
2424

@@ -39,10 +39,10 @@ public void ExclusivePluginQueryIgnoreDisabledTest()
3939
{">", new PluginPair {Metadata = new PluginMetadata {ActionKeywords = new List<string> {">"}, Disabled = true}}}
4040
};
4141

42-
Query q = QueryBuilder.Build("> ping google.com -n 20 -6", nonGlobalPlugins);
42+
Query q = QueryBuilder.Build("> ping google.com -n 20 -6", "> ping google.com -n 20 -6", nonGlobalPlugins);
4343

4444
ClassicAssert.AreEqual("> ping google.com -n 20 -6", q.Search);
45-
ClassicAssert.AreEqual(q.Search, q.RawQuery, "RawQuery should be equal to Search.");
45+
ClassicAssert.AreEqual(q.Search, q.TrimmedQuery, "TrimmedQuery should be equal to Search.");
4646
ClassicAssert.AreEqual(6, q.SearchTerms.Length, "The length of SearchTerms should match.");
4747
ClassicAssert.AreNotEqual(">", q.ActionKeyword, "ActionKeyword should not match that of a disabled plugin.");
4848
ClassicAssert.AreEqual("ping google.com -n 20 -6", q.SecondToEndSearch, "SecondToEndSearch should be trimmed of multiple whitespace characters");
@@ -51,7 +51,7 @@ public void ExclusivePluginQueryIgnoreDisabledTest()
5151
[Test]
5252
public void GenericPluginQueryTest()
5353
{
54-
Query q = QueryBuilder.Build("file.txt file2 file3", new Dictionary<string, PluginPair>());
54+
Query q = QueryBuilder.Build("file.txt file2 file3", "file.txt file2 file3", new Dictionary<string, PluginPair>());
5555

5656
ClassicAssert.AreEqual("file.txt file2 file3", q.Search);
5757
ClassicAssert.AreEqual("", q.ActionKeyword);

Flow.Launcher/Helper/ResultHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ public static class ResultHelper
1616
return await PopulateResultsAsync(item.PluginID, item.Query, item.Title, item.SubTitle, item.RecordKey);
1717
}
1818

19-
public static async Task<Result?> PopulateResultsAsync(string pluginId, string rawQuery, string title, string subTitle, string recordKey)
19+
public static async Task<Result?> PopulateResultsAsync(string pluginId, string trimmedQuery, string title, string subTitle, string recordKey)
2020
{
2121
var plugin = PluginManager.GetPluginForId(pluginId);
2222
if (plugin == null) return null;
23-
var query = QueryBuilder.Build(rawQuery, PluginManager.GetNonGlobalPlugins());
23+
var query = QueryBuilder.Build(trimmedQuery, trimmedQuery, PluginManager.GetNonGlobalPlugins());
2424
if (query == null) return null;
2525
try
2626
{

Flow.Launcher/MainWindow.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ private void OnKeyDown(object sender, KeyEventArgs e)
351351
&& QueryTextBox.CaretIndex == QueryTextBox.Text.Length)
352352
{
353353
var queryWithoutActionKeyword =
354-
QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.GetNonGlobalPlugins())?.Search;
354+
QueryBuilder.Build(QueryTextBox.Text, QueryTextBox.Text.Trim(), PluginManager.GetNonGlobalPlugins())?.Search;
355355

356356
if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword))
357357
{

Flow.Launcher/ResultListBox.xaml.cs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Threading;
34
using System.Windows;
45
using System.Windows.Controls;
56
using System.Windows.Input;
@@ -9,7 +10,7 @@ namespace Flow.Launcher
910
{
1011
public partial class ResultListBox
1112
{
12-
protected object _lock = new object();
13+
protected Lock _lock = new();
1314
private Point _lastpos;
1415
private ListBoxItem curItem = null;
1516
public ResultListBox()
@@ -89,12 +90,11 @@ private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
8990
}
9091
}
9192

92-
93-
private Point start;
94-
private string path;
95-
private string query;
93+
private Point _start;
94+
private string _path;
95+
private string _trimmedQuery;
9696
// this method is called by the UI thread, which is single threaded, so we can be sloppy with locking
97-
private bool isDragging;
97+
private bool _isDragging;
9898

9999
private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
100100
{
@@ -105,60 +105,63 @@ private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEve
105105
Result:
106106
{
107107
CopyText: { } copyText,
108-
OriginQuery.RawQuery: { } rawQuery
108+
OriginQuery.TrimmedQuery: { } trimmedQuery
109109
}
110110
}
111111
}) return;
112112

113-
path = copyText;
114-
query = rawQuery;
115-
start = e.GetPosition(null);
116-
isDragging = true;
113+
_path = copyText;
114+
_trimmedQuery = trimmedQuery;
115+
_start = e.GetPosition(null);
116+
_isDragging = true;
117117
}
118+
118119
private void ResultList_MouseMove(object sender, MouseEventArgs e)
119120
{
120-
if (e.LeftButton != MouseButtonState.Pressed || !isDragging)
121+
if (e.LeftButton != MouseButtonState.Pressed || !_isDragging)
121122
{
122-
start = default;
123-
path = string.Empty;
124-
query = string.Empty;
125-
isDragging = false;
123+
_start = default;
124+
_path = string.Empty;
125+
_trimmedQuery = string.Empty;
126+
_isDragging = false;
126127
return;
127128
}
128129

129-
if (!File.Exists(path) && !Directory.Exists(path))
130+
if (!File.Exists(_path) && !Directory.Exists(_path))
130131
return;
131132

132133
Point mousePosition = e.GetPosition(null);
133-
Vector diff = this.start - mousePosition;
134+
Vector diff = _start - mousePosition;
134135

135136
if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance
136137
|| Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance)
137138
return;
138139

139-
isDragging = false;
140+
_isDragging = false;
140141

141142
App.API.HideMainWindow();
142143

143144
var data = new DataObject(DataFormats.FileDrop, new[]
144145
{
145-
path
146+
_path
146147
});
147148

148149
// Reassigning query to a new variable because for some reason
149150
// after DragDrop.DoDragDrop call, 'query' loses its content, i.e. becomes empty string
150-
var rawQuery = query;
151+
var trimmedQuery = _trimmedQuery;
151152
var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy);
152153
if (effect == DragDropEffects.Move)
153-
App.API.ChangeQuery(rawQuery, true);
154+
App.API.ChangeQuery(trimmedQuery, true);
154155
}
156+
155157
private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
156158
{
157159
if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
158160
return;
159161

160162
RightClickResultCommand?.Execute(result.Result);
161163
}
164+
162165
private void ResultListBox_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
163166
{
164167
if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })

Flow.Launcher/Storage/LastOpenedHistoryItem.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ public bool Equals(Result r)
1919
return Title == r.Title
2020
&& SubTitle == r.SubTitle
2121
&& PluginID == r.PluginID
22-
&& Query == r.OriginQuery.RawQuery;
22+
&& Query == r.OriginQuery.TrimmedQuery;
2323
}
2424
else
2525
{
2626
return RecordKey == r.RecordKey
2727
&& PluginID == r.PluginID
28-
&& Query == r.OriginQuery.RawQuery;
28+
&& Query == r.OriginQuery.TrimmedQuery;
2929
}
3030
}
3131
}

0 commit comments

Comments
 (0)