@@ -31,8 +31,9 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable
31
31
32
32
private static readonly string ClassName = nameof ( MainViewModel ) ;
33
33
34
- private bool _isQueryRunning ;
35
34
private Query _lastQuery ;
35
+ private Query _runningQuery ; // Used for QueryResultAsync
36
+ private Query _currentQuery ; // Used for ResultsUpdated
36
37
private string _queryTextBeforeLeaveResults ;
37
38
38
39
private readonly FlowLauncherJsonStorage < History > _historyItemsStorage ;
@@ -235,7 +236,7 @@ public void RegisterResultsUpdatedEvent()
235
236
var plugin = ( IResultUpdated ) pair . Plugin ;
236
237
plugin . ResultsUpdated += ( s , e ) =>
237
238
{
238
- if ( e . Query . RawQuery != QueryText || e . Token . IsCancellationRequested )
239
+ if ( _currentQuery == null || e . Query . RawQuery != _currentQuery . RawQuery || e . Token . IsCancellationRequested )
239
240
{
240
241
return ;
241
242
}
@@ -255,9 +256,12 @@ public void RegisterResultsUpdatedEvent()
255
256
256
257
PluginManager . UpdatePluginMetadata ( resultsCopy , pair . Metadata , e . Query ) ;
257
258
258
- if ( token . IsCancellationRequested ) return ;
259
+ if ( _currentQuery == null || e . Query . RawQuery != _currentQuery . RawQuery || token . IsCancellationRequested )
260
+ {
261
+ return ;
262
+ }
259
263
260
- if ( ! _resultsUpdateChannelWriter . TryWrite ( new ResultsForUpdate ( resultsCopy , pair . Metadata , e . Query ,
264
+ if ( ! _resultsUpdateChannelWriter . TryWrite ( new ResultsForUpdate ( resultsCopy , pair . Metadata , e . Query ,
261
265
token ) ) )
262
266
{
263
267
App . API . LogError ( ClassName , "Unable to add item to Result Update Queue" ) ;
@@ -365,7 +369,7 @@ private void LoadContextMenu()
365
369
[ RelayCommand ]
366
370
private void Backspace ( object index )
367
371
{
368
- var query = QueryBuilder . Build ( QueryText . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
372
+ var query = QueryBuilder . Build ( QueryText , QueryText . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
369
373
370
374
// GetPreviousExistingDirectory does not require trailing '\', otherwise will return empty string
371
375
var path = FilesFolders . GetPreviousExistingDirectory ( ( _ ) => true , query . Search . TrimEnd ( '\\ ' ) ) ;
@@ -786,7 +790,7 @@ private ResultsViewModel SelectedResults
786
790
787
791
public Visibility ProgressBarVisibility { get ; set ; }
788
792
public Visibility MainWindowVisibility { get ; set ; }
789
-
793
+
790
794
// This is to be used for determining the visibility status of the main window instead of MainWindowVisibility
791
795
// because it is more accurate and reliable representation than using Visibility as a condition check
792
796
public bool MainWindowVisibilityStatus { get ; set ; } = true ;
@@ -1063,7 +1067,7 @@ private bool CanExternalPreviewSelectedResult(out string path)
1063
1067
path = QueryResultsPreviewed ( ) ? Results . SelectedItem ? . Result ? . Preview . FilePath : string . Empty ;
1064
1068
return ! string . IsNullOrEmpty ( path ) ;
1065
1069
}
1066
-
1070
+
1067
1071
private bool QueryResultsPreviewed ( )
1068
1072
{
1069
1073
var previewed = PreviewSelectedItem == Results . SelectedItem ;
@@ -1196,6 +1200,7 @@ private void QueryHistory()
1196
1200
private async Task QueryResultsAsync ( bool searchDelay , bool isReQuery = false , bool reSelect = true )
1197
1201
{
1198
1202
_updateSource ? . Cancel ( ) ;
1203
+ _runningQuery = null ;
1199
1204
1200
1205
var query = await ConstructQueryAsync ( QueryText , Settings . CustomShortcuts , Settings . BuiltinShortcuts ) ;
1201
1206
@@ -1215,89 +1220,103 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b
1215
1220
return ;
1216
1221
}
1217
1222
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 ;
1219
1228
1220
- ProgressBarVisibility = Visibility . Hidden ;
1221
- _isQueryRunning = true ;
1229
+ _updateSource = new CancellationTokenSource ( ) ;
1222
1230
1223
- // Switch to ThreadPool thread
1224
- await TaskScheduler . Default ;
1231
+ ProgressBarVisibility = Visibility . Hidden ;
1225
1232
1226
- if ( _updateSource . Token . IsCancellationRequested ) return ;
1233
+ _runningQuery = query ;
1234
+ _currentQuery = query ;
1227
1235
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 ;
1230
1238
1231
- // handle the exclusiveness of plugin using action keyword
1232
- RemoveOldQueryResults ( query ) ;
1239
+ if ( _updateSource . Token . IsCancellationRequested ) return ;
1233
1240
1234
- _lastQuery = query ;
1241
+ // Update the query's IsReQuery property to true if this is a re-query
1242
+ query . IsReQuery = isReQuery ;
1235
1243
1236
- var plugins = PluginManager . ValidPluginsForQuery ( query ) ;
1244
+ // handle the exclusiveness of plugin using action keyword
1245
+ RemoveOldQueryResults ( query ) ;
1237
1246
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 ;
1250
1248
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 ) ;
1260
1250
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 ( _ =>
1262
1275
{
1263
1276
// 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 )
1265
1278
{
1266
1279
ProgressBarVisibility = Visibility . Visible ;
1267
1280
}
1268
1281
} ,
1269
- _updateSource . Token ,
1270
- TaskContinuationOptions . NotOnCanceled ,
1271
- TaskScheduler . Default ) ;
1282
+ _updateSource . Token ,
1283
+ TaskContinuationOptions . NotOnCanceled ,
1284
+ TaskScheduler . Default ) ;
1272
1285
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
1274
1287
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 ( ) ;
1280
1293
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
+ }
1290
1303
1291
- if ( _updateSource . Token . IsCancellationRequested ) return ;
1304
+ if ( _updateSource . Token . IsCancellationRequested ) return ;
1292
1305
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 ;
1296
1309
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
1298
1317
{
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 ;
1301
1320
}
1302
1321
1303
1322
// Local function
@@ -1374,7 +1393,7 @@ private async Task<Query> ConstructQueryAsync(string queryText, IEnumerable<Cust
1374
1393
// Applying builtin shortcuts
1375
1394
await BuildQueryAsync ( builtInShortcuts , queryBuilder , queryBuilderTmp ) ;
1376
1395
1377
- return QueryBuilder . Build ( queryBuilder . ToString ( ) . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
1396
+ return QueryBuilder . Build ( QueryText , queryBuilder . ToString ( ) . Trim ( ) , PluginManager . NonGlobalPlugins ) ;
1378
1397
}
1379
1398
1380
1399
private async Task BuildQueryAsync ( IEnumerable < BaseBuiltinShortcutModel > builtInShortcuts ,
@@ -1549,9 +1568,6 @@ public bool ShouldIgnoreHotkeys()
1549
1568
1550
1569
public void Show ( )
1551
1570
{
1552
- // When application is exiting, we should not show the main window
1553
- if ( App . Exiting ) return ;
1554
-
1555
1571
// When application is exiting, the Application.Current will be null
1556
1572
Application . Current ? . Dispatcher . Invoke ( ( ) =>
1557
1573
{
0 commit comments