@@ -31,8 +31,9 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable
3131
3232 private static readonly string ClassName = nameof ( MainViewModel ) ;
3333
34- private bool _isQueryRunning ;
3534 private Query _lastQuery ;
35+ private Query _runningQuery ; // Used for QueryResultAsync
36+ private Query _currentQuery ; // Used for ResultsUpdated
3637 private string _queryTextBeforeLeaveResults ;
3738
3839 private readonly FlowLauncherJsonStorage < History > _historyItemsStorage ;
@@ -235,7 +236,7 @@ public void RegisterResultsUpdatedEvent()
235236 var plugin = ( IResultUpdated ) pair . Plugin ;
236237 plugin . ResultsUpdated += ( s , e ) =>
237238 {
238- if ( e . Query . RawQuery != QueryText || e . Token . IsCancellationRequested )
239+ if ( _currentQuery == null || e . Query . RawQuery != _currentQuery . RawQuery || e . Token . IsCancellationRequested )
239240 {
240241 return ;
241242 }
@@ -255,9 +256,12 @@ public void RegisterResultsUpdatedEvent()
255256
256257 PluginManager . UpdatePluginMetadata ( resultsCopy , pair . Metadata , e . Query ) ;
257258
258- if ( token . IsCancellationRequested ) return ;
259+ if ( _currentQuery == null || e . Query . RawQuery != _currentQuery . RawQuery || token . IsCancellationRequested )
260+ {
261+ return ;
262+ }
259263
260- if ( ! _resultsUpdateChannelWriter . TryWrite ( new ResultsForUpdate ( resultsCopy , pair . Metadata , e . Query ,
264+ if ( ! _resultsUpdateChannelWriter . TryWrite ( new ResultsForUpdate ( resultsCopy , pair . Metadata , e . Query ,
261265 token ) ) )
262266 {
263267 App . API . LogError ( ClassName , "Unable to add item to Result Update Queue" ) ;
@@ -365,7 +369,7 @@ private void LoadContextMenu()
365369 [ RelayCommand ]
366370 private void Backspace ( object index )
367371 {
368- var query = QueryBuilder . Build ( QueryText . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
372+ var query = QueryBuilder . Build ( QueryText , QueryText . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
369373
370374 // GetPreviousExistingDirectory does not require trailing '\', otherwise will return empty string
371375 var path = FilesFolders . GetPreviousExistingDirectory ( ( _ ) => true , query . Search . TrimEnd ( '\\ ' ) ) ;
@@ -786,7 +790,7 @@ private ResultsViewModel SelectedResults
786790
787791 public Visibility ProgressBarVisibility { get ; set ; }
788792 public Visibility MainWindowVisibility { get ; set ; }
789-
793+
790794 // This is to be used for determining the visibility status of the main window instead of MainWindowVisibility
791795 // because it is more accurate and reliable representation than using Visibility as a condition check
792796 public bool MainWindowVisibilityStatus { get ; set ; } = true ;
@@ -1063,7 +1067,7 @@ private bool CanExternalPreviewSelectedResult(out string path)
10631067 path = QueryResultsPreviewed ( ) ? Results . SelectedItem ? . Result ? . Preview . FilePath : string . Empty ;
10641068 return ! string . IsNullOrEmpty ( path ) ;
10651069 }
1066-
1070+
10671071 private bool QueryResultsPreviewed ( )
10681072 {
10691073 var previewed = PreviewSelectedItem == Results . SelectedItem ;
@@ -1196,6 +1200,7 @@ private void QueryHistory()
11961200 private async Task QueryResultsAsync ( bool searchDelay , bool isReQuery = false , bool reSelect = true )
11971201 {
11981202 _updateSource ? . Cancel ( ) ;
1203+ _runningQuery = null ;
11991204
12001205 var query = await ConstructQueryAsync ( QueryText , Settings . CustomShortcuts , Settings . BuiltinShortcuts ) ;
12011206
@@ -1215,89 +1220,103 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b
12151220 return ;
12161221 }
12171222
1218- _updateSource = new CancellationTokenSource ( ) ;
1223+ try
1224+ {
1225+ // Check if the query has changed because query can be changed so fast that
1226+ // token of the query between two queries has not been created yet
1227+ if ( query . Input != QueryText ) return ;
12191228
1220- ProgressBarVisibility = Visibility . Hidden ;
1221- _isQueryRunning = true ;
1229+ _updateSource = new CancellationTokenSource ( ) ;
12221230
1223- // Switch to ThreadPool thread
1224- await TaskScheduler . Default ;
1231+ ProgressBarVisibility = Visibility . Hidden ;
12251232
1226- if ( _updateSource . Token . IsCancellationRequested ) return ;
1233+ _runningQuery = query ;
1234+ _currentQuery = query ;
12271235
1228- // Update the query's IsReQuery property to true if this is a re-query
1229- query . IsReQuery = isReQuery ;
1236+ // Switch to ThreadPool thread
1237+ await TaskScheduler . Default ;
12301238
1231- // handle the exclusiveness of plugin using action keyword
1232- RemoveOldQueryResults ( query ) ;
1239+ if ( _updateSource . Token . IsCancellationRequested ) return ;
12331240
1234- _lastQuery = query ;
1241+ // Update the query's IsReQuery property to true if this is a re-query
1242+ query . IsReQuery = isReQuery ;
12351243
1236- var plugins = PluginManager . ValidPluginsForQuery ( query ) ;
1244+ // handle the exclusiveness of plugin using action keyword
1245+ RemoveOldQueryResults ( query ) ;
12371246
1238- if ( plugins . Count == 1 )
1239- {
1240- PluginIconPath = plugins . Single ( ) . Metadata . IcoPath ;
1241- PluginIconSource = await App . API . LoadImageAsync ( PluginIconPath ) ;
1242- SearchIconVisibility = Visibility . Hidden ;
1243- }
1244- else
1245- {
1246- PluginIconPath = null ;
1247- PluginIconSource = null ;
1248- SearchIconVisibility = Visibility . Visible ;
1249- }
1247+ _lastQuery = query ;
12501248
1251- // Do not wait for performance improvement
1252- /*if (string.IsNullOrEmpty(query.ActionKeyword))
1253- {
1254- // Wait 15 millisecond for query change in global query
1255- // if query changes, return so that it won't be calculated
1256- await Task.Delay(15, _updateSource.Token);
1257- if (_updateSource.Token.IsCancellationRequested)
1258- return;
1259- }*/
1249+ var plugins = PluginManager . ValidPluginsForQuery ( query ) ;
12601250
1261- _ = Task . Delay ( 200 , _updateSource . Token ) . ContinueWith ( _ =>
1251+ if ( plugins . Count == 1 )
1252+ {
1253+ PluginIconPath = plugins . Single ( ) . Metadata . IcoPath ;
1254+ PluginIconSource = await App . API . LoadImageAsync ( PluginIconPath ) ;
1255+ SearchIconVisibility = Visibility . Hidden ;
1256+ }
1257+ else
1258+ {
1259+ PluginIconPath = null ;
1260+ PluginIconSource = null ;
1261+ SearchIconVisibility = Visibility . Visible ;
1262+ }
1263+
1264+ // Do not wait for performance improvement
1265+ /*if (string.IsNullOrEmpty(query.ActionKeyword))
1266+ {
1267+ // Wait 15 millisecond for query change in global query
1268+ // if query changes, return so that it won't be calculated
1269+ await Task.Delay(15, _updateSource.Token);
1270+ if (_updateSource.Token.IsCancellationRequested)
1271+ return;
1272+ }*/
1273+
1274+ _ = Task . Delay ( 200 , _updateSource . Token ) . ContinueWith ( _ =>
12621275 {
12631276 // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
1264- if ( _isQueryRunning )
1277+ if ( _runningQuery != null && _runningQuery == query )
12651278 {
12661279 ProgressBarVisibility = Visibility . Visible ;
12671280 }
12681281 } ,
1269- _updateSource . Token ,
1270- TaskContinuationOptions . NotOnCanceled ,
1271- TaskScheduler . Default ) ;
1282+ _updateSource . Token ,
1283+ TaskContinuationOptions . NotOnCanceled ,
1284+ TaskScheduler . Default ) ;
12721285
1273- // plugins are ICollection, meaning LINQ will get the Count and preallocate Array
1286+ // plugins are ICollection, meaning LINQ will get the Count and preallocate Array
12741287
1275- var tasks = plugins . Select ( plugin => plugin . Metadata . Disabled switch
1276- {
1277- false => QueryTaskAsync ( plugin , _updateSource . Token ) ,
1278- true => Task . CompletedTask
1279- } ) . ToArray ( ) ;
1288+ var tasks = plugins . Select ( plugin => plugin . Metadata . Disabled switch
1289+ {
1290+ false => QueryTaskAsync ( plugin , _updateSource . Token ) ,
1291+ true => Task . CompletedTask
1292+ } ) . ToArray ( ) ;
12801293
1281- try
1282- {
1283- // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
1284- await Task . WhenAll ( tasks ) ;
1285- }
1286- catch ( OperationCanceledException )
1287- {
1288- // nothing to do here
1289- }
1294+ try
1295+ {
1296+ // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
1297+ await Task . WhenAll ( tasks ) ;
1298+ }
1299+ catch ( OperationCanceledException )
1300+ {
1301+ // nothing to do here
1302+ }
12901303
1291- if ( _updateSource . Token . IsCancellationRequested ) return ;
1304+ if ( _updateSource . Token . IsCancellationRequested ) return ;
12921305
1293- // this should happen once after all queries are done so progress bar should continue
1294- // until the end of all querying
1295- _isQueryRunning = false ;
1306+ // this should happen once after all queries are done so progress bar should continue
1307+ // until the end of all querying
1308+ _runningQuery = null ;
12961309
1297- if ( ! _updateSource . Token . IsCancellationRequested )
1310+ if ( ! _updateSource . Token . IsCancellationRequested )
1311+ {
1312+ // update to hidden if this is still the current query
1313+ ProgressBarVisibility = Visibility . Hidden ;
1314+ }
1315+ }
1316+ finally
12981317 {
1299- // update to hidden if this is still the current query
1300- ProgressBarVisibility = Visibility . Hidden ;
1318+ // this make sures running query is null even if the query is canceled
1319+ _runningQuery = null ;
13011320 }
13021321
13031322 // Local function
@@ -1374,7 +1393,7 @@ private async Task<Query> ConstructQueryAsync(string queryText, IEnumerable<Cust
13741393 // Applying builtin shortcuts
13751394 await BuildQueryAsync ( builtInShortcuts , queryBuilder , queryBuilderTmp ) ;
13761395
1377- return QueryBuilder . Build ( queryBuilder . ToString ( ) . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
1396+ return QueryBuilder . Build ( QueryText , queryBuilder . ToString ( ) . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
13781397 }
13791398
13801399 private async Task BuildQueryAsync ( IEnumerable < BaseBuiltinShortcutModel > builtInShortcuts ,
@@ -1549,9 +1568,6 @@ public bool ShouldIgnoreHotkeys()
15491568
15501569 public void Show ( )
15511570 {
1552- // When application is exiting, we should not show the main window
1553- if ( App . Exiting ) return ;
1554-
15551571 // When application is exiting, the Application.Current will be null
15561572 Application . Current ? . Dispatcher . Invoke ( ( ) =>
15571573 {
0 commit comments