Support query for plugins with home query interface

This commit is contained in:
Jack251970 2025-05-03 16:50:50 +08:00
parent 0f09fea30b
commit 17a0834bcd
4 changed files with 104 additions and 23 deletions

View file

@ -25,6 +25,7 @@ namespace Flow.Launcher.Core.Plugin
private static readonly string ClassName = nameof(PluginManager);
private static IEnumerable<PluginPair> _contextMenuPlugins;
private static IEnumerable<PluginPair> _homePlugins;
public static List<PluginPair> AllPlugins { get; private set; }
public static readonly HashSet<PluginPair> GlobalPlugins = new();
@ -227,6 +228,8 @@ namespace Flow.Launcher.Core.Plugin
await Task.WhenAll(InitTasks);
_contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
_homePlugins = GetPluginsForInterface<IAsyncHomeQuery>();
foreach (var plugin in AllPlugins)
{
// set distinct on each plugin's action keywords helps only firing global(*) and action keywords once where a plugin
@ -274,6 +277,14 @@ namespace Flow.Launcher.Core.Plugin
};
}
public static ICollection<PluginPair> ValidPluginsForHomeQuery(Query query)
{
if (query is not null)
return Array.Empty<PluginPair>();
return _homePlugins.ToList();
}
public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Query query, CancellationToken token)
{
var results = new List<Result>();
@ -318,6 +329,36 @@ namespace Flow.Launcher.Core.Plugin
return results;
}
public static async Task<List<Result>> QueryHomeForPluginAsync(PluginPair pair, CancellationToken token)
{
var results = new List<Result>();
var metadata = pair.Metadata;
try
{
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
async () => results = await ((IAsyncHomeQuery)pair.Plugin).HomeQueryAsync(token).ConfigureAwait(false));
token.ThrowIfCancellationRequested();
if (results == null)
return null;
UpdatePluginMetadata(results, metadata);
token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
// null will be fine since the results will only be added into queue if the token hasn't been cancelled
return null;
}
catch (Exception e)
{
API.LogException(ClassName, $"Failed to query home for plugin: {metadata.Name}", e);
return null;
}
return results;
}
public static void UpdatePluginMetadata(IReadOnlyList<Result> results, PluginMetadata metadata, Query query)
{
foreach (var r in results)
@ -333,6 +374,16 @@ namespace Flow.Launcher.Core.Plugin
}
}
private static void UpdatePluginMetadata(IReadOnlyList<Result> results, PluginMetadata metadata)
{
foreach (var r in results)
{
r.PluginDirectory = metadata.PluginDirectory;
r.PluginID = metadata.ID;
r.OriginQuery = null;
}
}
/// <summary>
/// get specified plugin, return null if not found
/// </summary>

View file

@ -158,6 +158,11 @@ namespace Flow.Launcher.Infrastructure.UserSettings
}
}
}
public bool ShowHomeQuery { get; set; } = true;
public bool ShowHistoryRecordsForHomeQuery { get; set; } = false;
public int HistoryRecordsCountForHomeQuery { get; set; } = 5;
public int CustomExplorerIndex { get; set; } = 0;
[JsonIgnore]

View file

@ -174,6 +174,9 @@ namespace Flow.Launcher.Plugin
/// <summary>
/// Query information associated with the result
/// </summary>
/// <remarks>
/// If the query is for home query, this will be null
/// </remarks>
internal Query OriginQuery { get; set; }
/// <summary>

View file

@ -1198,21 +1198,31 @@ namespace Flow.Launcher.ViewModel
_updateSource?.Cancel();
var query = await ConstructQueryAsync(QueryText, Settings.CustomShortcuts, Settings.BuiltinShortcuts);
var homeQuery = query == null;
ICollection<PluginPair> plugins = Array.Empty<PluginPair>();
if (query == null) // shortcut expanded
{
// Hide and clear results again because running query may show and add some results
Results.Visibility = Visibility.Collapsed;
Results.Clear();
if (Settings.ShowHomeQuery)
{
plugins = PluginManager.ValidPluginsForHomeQuery(query);
}
if (plugins.Count == 0)
{
// 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;
// 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;
return;
// Hide progress bar again because running query may set this to visible
ProgressBarVisibility = Visibility.Hidden;
return;
}
}
_updateSource = new CancellationTokenSource();
@ -1226,27 +1236,37 @@ namespace Flow.Launcher.ViewModel
if (_updateSource.Token.IsCancellationRequested) return;
// Update the query's IsReQuery property to true if this is a re-query
query.IsReQuery = isReQuery;
if (!homeQuery) query.IsReQuery = isReQuery;
// handle the exclusiveness of plugin using action keyword
RemoveOldQueryResults(query);
_lastQuery = query;
var plugins = PluginManager.ValidPluginsForQuery(query);
if (plugins.Count == 1)
{
PluginIconPath = plugins.Single().Metadata.IcoPath;
PluginIconSource = await App.API.LoadImageAsync(PluginIconPath);
SearchIconVisibility = Visibility.Hidden;
}
else
if (homeQuery)
{
// Do not show plugin icon if this is a home query
PluginIconPath = null;
PluginIconSource = null;
SearchIconVisibility = Visibility.Visible;
}
else
{
plugins = PluginManager.ValidPluginsForQuery(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;
}
}
// Do not wait for performance improvement
/*if (string.IsNullOrEmpty(query.ActionKeyword))
@ -1303,7 +1323,7 @@ namespace Flow.Launcher.ViewModel
// Local function
async Task QueryTaskAsync(PluginPair plugin, CancellationToken token)
{
if (searchDelay)
if (searchDelay && !homeQuery) // Do not delay for home query
{
var searchDelayTime = plugin.Metadata.SearchDelayTime ?? Settings.SearchDelayTime;
@ -1316,7 +1336,9 @@ namespace Flow.Launcher.ViewModel
// Task.Yield will force it to run in ThreadPool
await Task.Yield();
var results = await PluginManager.QueryForPluginAsync(plugin, query, token);
var results = homeQuery ?
await PluginManager.QueryHomeForPluginAsync(plugin, token) :
await PluginManager.QueryForPluginAsync(plugin, query, token);
if (token.IsCancellationRequested) return;
@ -1616,7 +1638,7 @@ namespace Flow.Launcher.ViewModel
break;
case LastQueryMode.ActionKeywordPreserved:
case LastQueryMode.ActionKeywordSelected:
var newQuery = _lastQuery.ActionKeyword;
var newQuery = _lastQuery?.ActionKeyword;
if (!string.IsNullOrEmpty(newQuery))
newQuery += " ";