Use new model to clear results & Fix clear existing results when using IResultUpdate (#3588)

This commit is contained in:
Jack Ye 2025-06-02 11:16:22 +08:00 committed by GitHub
parent a68c69bb06
commit a06cb43540
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 63 additions and 33 deletions

View file

@ -16,7 +16,8 @@ namespace Flow.Launcher.Core.Plugin
Search = string.Empty, Search = string.Empty,
RawQuery = string.Empty, RawQuery = string.Empty,
SearchTerms = Array.Empty<string>(), SearchTerms = Array.Empty<string>(),
ActionKeyword = string.Empty ActionKeyword = string.Empty,
IsHomeQuery = true
}; };
} }
@ -53,7 +54,8 @@ namespace Flow.Launcher.Core.Plugin
Search = search, Search = search,
RawQuery = rawQuery, RawQuery = rawQuery,
SearchTerms = searchTerms, SearchTerms = searchTerms,
ActionKeyword = actionKeyword ActionKeyword = actionKeyword,
IsHomeQuery = false
}; };
} }
} }

View file

@ -21,6 +21,11 @@ namespace Flow.Launcher.Plugin
/// </summary> /// </summary>
public bool IsReQuery { get; internal set; } = false; public bool IsReQuery { get; internal set; } = false;
/// <summary>
/// Determines whether the query is a home query.
/// </summary>
public bool IsHomeQuery { get; internal init; } = false;
/// <summary> /// <summary>
/// Search part of a query. /// 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 RawQuery.

View file

@ -101,7 +101,7 @@ namespace Flow.Launcher
private void ThemeManager_ActualApplicationThemeChanged(ModernWpf.ThemeManager sender, object args) private void ThemeManager_ActualApplicationThemeChanged(ModernWpf.ThemeManager sender, object args)
{ {
_theme.RefreshFrameAsync(); _ = _theme.RefreshFrameAsync();
} }
private void OnSourceInitialized(object sender, EventArgs e) private void OnSourceInitialized(object sender, EventArgs e)

View file

@ -216,7 +216,26 @@ namespace Flow.Launcher.ViewModel
while (channelReader.TryRead(out var item)) while (channelReader.TryRead(out var item))
{ {
if (!item.Token.IsCancellationRequested) if (!item.Token.IsCancellationRequested)
{
// Indicate if to clear existing results so to show only ones from plugins with action keywords
var query = item.Query;
var currentIsHomeQuery = query.IsHomeQuery;
var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery);
_lastQuery = item.Query;
_previousIsHomeQuery = currentIsHomeQuery;
// If the queue already has the item, we need to pass the shouldClearExistingResults flag
if (queue.TryGetValue(item.ID, out var existingItem))
{
item.ShouldClearExistingResults = shouldClearExistingResults || existingItem.ShouldClearExistingResults;
}
else
{
item.ShouldClearExistingResults = shouldClearExistingResults;
}
queue[item.ID] = item; queue[item.ID] = item;
}
} }
UpdateResultView(queue.Values); UpdateResultView(queue.Values);
@ -268,6 +287,8 @@ namespace Flow.Launcher.ViewModel
if (token.IsCancellationRequested) return; if (token.IsCancellationRequested) return;
App.API.LogDebug(ClassName, $"Update results for plugin <{pair.Metadata.Name}>");
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query,
token))) token)))
{ {
@ -1262,7 +1283,7 @@ namespace Flow.Launcher.ViewModel
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 RawQuery <{query.RawQuery}>");
var currentIsHomeQuery = query.RawQuery == string.Empty; var currentIsHomeQuery = query.IsHomeQuery;
_updateSource?.Dispose(); _updateSource?.Dispose();
@ -1436,13 +1457,8 @@ namespace Flow.Launcher.ViewModel
App.API.LogDebug(ClassName, $"Update results for plugin <{plugin.Metadata.Name}>"); App.API.LogDebug(ClassName, $"Update results for plugin <{plugin.Metadata.Name}>");
// Indicate if to clear existing results so to show only ones from plugins with action keywords
var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery);
_lastQuery = query;
_previousIsHomeQuery = currentIsHomeQuery;
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query, if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query,
token, reSelect, shouldClearExistingResults))) token, reSelect)))
{ {
App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); App.API.LogError(ClassName, "Unable to add item to Result Update Queue");
} }
@ -1459,13 +1475,8 @@ namespace Flow.Launcher.ViewModel
App.API.LogDebug(ClassName, $"Update results for history"); App.API.LogDebug(ClassName, $"Update results for history");
// Indicate if to clear existing results so to show only ones from plugins with action keywords
var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery);
_lastQuery = query;
_previousIsHomeQuery = currentIsHomeQuery;
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query, if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query,
token, reSelect, shouldClearExistingResults))) token, reSelect)))
{ {
App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); App.API.LogError(ClassName, "Unable to add item to Result Update Queue");
} }
@ -1865,6 +1876,7 @@ namespace Flow.Launcher.ViewModel
{ {
if (!resultsForUpdates.Any()) if (!resultsForUpdates.Any())
return; return;
CancellationToken token; CancellationToken token;
try try

View file

@ -10,7 +10,7 @@ namespace Flow.Launcher.ViewModel
Query Query, Query Query,
CancellationToken Token, CancellationToken Token,
bool ReSelectFirstResult = true, bool ReSelectFirstResult = true,
bool shouldClearExistingResults = false) bool ShouldClearExistingResults = false)
{ {
public string ID { get; } = Metadata.ID; public string ID { get; } = Metadata.ID;
} }

View file

@ -17,6 +17,8 @@ namespace Flow.Launcher.ViewModel
{ {
#region Private Fields #region Private Fields
private readonly string ClassName = nameof(ResultsViewModel);
public ResultCollection Results { get; } public ResultCollection Results { get; }
private readonly object _collectionLock = new(); private readonly object _collectionLock = new();
@ -187,11 +189,9 @@ namespace Flow.Launcher.ViewModel
/// </summary> /// </summary>
public void AddResults(ICollection<ResultsForUpdate> resultsForUpdates, CancellationToken token, bool reselect = true) public void AddResults(ICollection<ResultsForUpdate> resultsForUpdates, CancellationToken token, bool reselect = true)
{ {
// Since NewResults may need to clear existing results, do not check token cancellation after this point
var newResults = NewResults(resultsForUpdates); var newResults = NewResults(resultsForUpdates);
if (token.IsCancellationRequested)
return;
UpdateResults(newResults, reselect, token); UpdateResults(newResults, reselect, token);
} }
@ -240,16 +240,20 @@ namespace Flow.Launcher.ViewModel
private List<ResultViewModel> NewResults(ICollection<ResultsForUpdate> resultsForUpdates) private List<ResultViewModel> NewResults(ICollection<ResultsForUpdate> resultsForUpdates)
{ {
if (!resultsForUpdates.Any()) if (!resultsForUpdates.Any())
{
App.API.LogDebug(ClassName, "No results for updates, returning existing results");
return Results; return Results;
}
var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)); var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings));
if (resultsForUpdates.Any(x => x.shouldClearExistingResults)) if (resultsForUpdates.Any(x => x.ShouldClearExistingResults))
{ {
App.API.LogDebug("NewResults", $"Existing results are cleared for query"); App.API.LogDebug(ClassName, $"Existing results are cleared for query");
return newResults.OrderByDescending(rv => rv.Result.Score).ToList(); return newResults.OrderByDescending(rv => rv.Result.Score).ToList();
} }
App.API.LogDebug(ClassName, $"Keeping existing results for {resultsForUpdates.Count} queries");
return Results.Where(r => r?.Result != null && resultsForUpdates.All(u => u.ID != r.Result.PluginID)) return Results.Where(r => r?.Result != null && resultsForUpdates.All(u => u.ID != r.Result.PluginID))
.Concat(newResults) .Concat(newResults)
.OrderByDescending(rv => rv.Result.Score) .OrderByDescending(rv => rv.Result.Score)
@ -293,8 +297,6 @@ namespace Flow.Launcher.ViewModel
{ {
private long editTime = 0; private long editTime = 0;
private CancellationToken _token;
public event NotifyCollectionChangedEventHandler CollectionChanged; public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
@ -302,12 +304,12 @@ namespace Flow.Launcher.ViewModel
CollectionChanged?.Invoke(this, e); CollectionChanged?.Invoke(this, e);
} }
public void BulkAddAll(List<ResultViewModel> resultViews) private void BulkAddAll(List<ResultViewModel> resultViews, CancellationToken token = default)
{ {
AddRange(resultViews); AddRange(resultViews);
// can return because the list will be cleared next time updated, which include a reset event // can return because the list will be cleared next time updated, which include a reset event
if (_token.IsCancellationRequested) if (token.IsCancellationRequested)
return; return;
// manually update event // manually update event
@ -315,12 +317,12 @@ namespace Flow.Launcher.ViewModel
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
} }
private void AddAll(List<ResultViewModel> Items) private void AddAll(List<ResultViewModel> Items, CancellationToken token = default)
{ {
for (int i = 0; i < Items.Count; i++) for (int i = 0; i < Items.Count; i++)
{ {
var item = Items[i]; var item = Items[i];
if (_token.IsCancellationRequested) if (token.IsCancellationRequested)
return; return;
Add(item); Add(item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i));
@ -342,21 +344,30 @@ namespace Flow.Launcher.ViewModel
/// <param name="newItems"></param> /// <param name="newItems"></param>
public void Update(List<ResultViewModel> newItems, CancellationToken token = default) public void Update(List<ResultViewModel> newItems, CancellationToken token = default)
{ {
_token = token; // Since NewResults may need to clear existing results, so we cannot check token cancellation here
if (Count == 0 && newItems.Count == 0 || _token.IsCancellationRequested) if (Count == 0 && newItems.Count == 0)
return; return;
if (editTime < 10 || newItems.Count < 30) if (editTime < 10 || newItems.Count < 30)
{ {
if (Count != 0) RemoveAll(newItems.Count); if (Count != 0) RemoveAll(newItems.Count);
AddAll(newItems);
// After results are removed, we need to check the token cancellation
// so that we will not add new items from the cancelled queries
if (token.IsCancellationRequested) return;
AddAll(newItems, token);
editTime++; editTime++;
return;
} }
else else
{ {
Clear(); Clear();
BulkAddAll(newItems);
// After results are removed, we need to check the token cancellation
// so that we will not add new items from the cancelled queries
if (token.IsCancellationRequested) return;
BulkAddAll(newItems, token);
if (Capacity > 8000 && newItems.Count < 3000) if (Capacity > 8000 && newItems.Count < 3000)
{ {
Capacity = newItems.Count; Capacity = newItems.Count;