diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 70a9c9c0f..e837843f4 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -29,7 +29,7 @@ namespace Flow.Launcher.Core.Plugin private static readonly ConcurrentDictionary _allInitializedPlugins = []; private static readonly ConcurrentDictionary _initFailedPlugins = []; private static readonly ConcurrentDictionary _globalPlugins = []; - private static readonly ConcurrentDictionary _nonGlobalPlugins = []; + private static readonly ConcurrentDictionary> _nonGlobalPlugins = []; private static PluginsSettings Settings; private static readonly ConcurrentBag ModifiedPlugins = []; @@ -332,7 +332,19 @@ namespace Flow.Launcher.Core.Plugin _globalPlugins.TryAdd(pair.Metadata.ID, pair); break; default: - _nonGlobalPlugins.TryAdd(actionKeyword, pair); + _nonGlobalPlugins.AddOrUpdate(actionKeyword, + _ => [pair], + (_, existing) => + { + lock (existing) + { + if (!existing.Contains(pair)) + { + existing.Add(pair); + } + } + return existing; + }); break; } } @@ -368,7 +380,7 @@ namespace Flow.Launcher.Core.Plugin if (query is null) return Array.Empty(); - if (!_nonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugin)) + if (!_nonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugins)) { if (dialogJump) return [.. GetGlobalPlugins().Where(p => p.Plugin is IAsyncDialogJump && !PluginModified(p.Metadata.ID))]; @@ -376,13 +388,11 @@ namespace Flow.Launcher.Core.Plugin return [.. GetGlobalPlugins().Where(p => !PluginModified(p.Metadata.ID))]; } - if (dialogJump && plugin.Plugin is not IAsyncDialogJump) - return Array.Empty(); + var validPlugins = plugins.Where(p => !PluginModified(p.Metadata.ID)); + if (dialogJump) + validPlugins = validPlugins.Where(p => p.Plugin is IAsyncDialogJump); - if (PluginModified(plugin.Metadata.ID)) - return Array.Empty(); - - return [plugin]; + return [.. validPlugins]; } public static ICollection ValidPluginsForHomeQuery() @@ -576,7 +586,7 @@ namespace Flow.Launcher.Core.Plugin return [.. _globalPlugins.Values]; } - public static Dictionary GetNonGlobalPlugins() + public static Dictionary> GetNonGlobalPlugins() { return _nonGlobalPlugins.ToDictionary(); } @@ -736,13 +746,27 @@ namespace Flow.Launcher.Core.Plugin public static void AddActionKeyword(string id, string newActionKeyword) { var plugin = GetPluginForId(id); + if (plugin == null) return; + if (newActionKeyword == Query.GlobalPluginWildcardSign) { _globalPlugins.TryAdd(id, plugin); } else { - _nonGlobalPlugins.AddOrUpdate(newActionKeyword, plugin, (key, oldValue) => plugin); + _nonGlobalPlugins.AddOrUpdate(newActionKeyword, + _ => [plugin], + (_, existing) => + { + lock (existing) + { + if (!existing.Contains(plugin)) + { + existing.Add(plugin); + } + } + return existing; + }); } // Update action keywords and action keyword in plugin metadata @@ -764,6 +788,8 @@ namespace Flow.Launcher.Core.Plugin public static void RemoveActionKeyword(string id, string oldActionkeyword) { var plugin = GetPluginForId(id); + if (plugin == null) return; + if (oldActionkeyword == Query.GlobalPluginWildcardSign && // Plugins may have multiple ActionKeywords that are global, eg. WebSearch plugin.Metadata.ActionKeywords @@ -774,7 +800,13 @@ namespace Flow.Launcher.Core.Plugin if (oldActionkeyword != Query.GlobalPluginWildcardSign) { - _nonGlobalPlugins.TryRemove(oldActionkeyword, out _); + if (_nonGlobalPlugins.TryGetValue(oldActionkeyword, out var plugins)) + { + lock (plugins) + { + plugins.RemoveAll(p => p.Metadata.ID == id); + } + } } // Update action keywords and action keyword in plugin metadata @@ -1032,10 +1064,13 @@ namespace Flow.Launcher.Core.Plugin { _globalPlugins.TryRemove(plugin.ID, out var _); } - var keysToRemove = _nonGlobalPlugins.Where(p => p.Value.Metadata.ID == plugin.ID).Select(p => p.Key).ToList(); - foreach (var key in keysToRemove) + var entriesToUpdate = _nonGlobalPlugins.ToList(); + foreach (var entry in entriesToUpdate) { - _nonGlobalPlugins.TryRemove(key, out var _); + lock (entry.Value) + { + entry.Value.RemoveAll(p => p.Metadata.ID == plugin.ID); + } } } diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index aac620cce..3b38a56b7 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Plugin { public static class QueryBuilder { - public static Query Build(string originalQuery, string trimmedQuery, Dictionary nonGlobalPlugins) + public static Query Build(string originalQuery, string trimmedQuery, Dictionary> nonGlobalPlugins) { // home query if (string.IsNullOrEmpty(trimmedQuery)) @@ -34,7 +35,8 @@ namespace Flow.Launcher.Core.Plugin string possibleActionKeyword = terms[0]; string[] searchTerms; - if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled) + if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPairs) + && pluginPairs.Any(plugin => !plugin.Metadata.Disabled)) { // use non global plugin for query actionKeyword = possibleActionKeyword;