diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 30e812c6f05..ada425f7e3e 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -8,6 +8,8 @@ using System.Linq; using System.Windows.Forms; using Flow.Launcher.Core.Resource; +using System.Threading.Tasks; +using Flow.Launcher.Core.Plugin; namespace Flow.Launcher.Core.ExternalPlugins.Environments { @@ -37,10 +39,10 @@ internal AbstractPluginEnvironment(List pluginMetadataList, Plug PluginSettings = pluginSettings; } - internal IEnumerable Setup() + internal IEnumerable Setup() { if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase))) - return new List(); + return new List(); if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath)) { @@ -87,7 +89,7 @@ internal IEnumerable Setup() $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); - return new List(); + return new List(); } } @@ -106,17 +108,17 @@ private void EnsureLatestInstalled(string expectedPath, string currentPath, stri internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata); - private IEnumerable SetPathForPluginPairs(string filePath, string languageToSet) + private IEnumerable SetPathForPluginPairs(string filePath, string languageToSet) { - var pluginPairs = new List(); + var tasks = new List(); foreach (var metadata in PluginMetadataList) { if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) - pluginPairs.Add(CreatePluginPair(filePath, metadata)); + tasks.Add(Task.Run(() => PluginsLoader.AddPlugin(CreatePluginPair(filePath, metadata)))); } - return pluginPairs; + return tasks; } private string GetFileFromDialog(string title, string filter = "") diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 91cb36a0e3c..e2227ab25b5 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -146,12 +146,12 @@ static PluginManager() /// todo happlebao The API should be removed /// /// - public static void LoadPlugins(PluginsSettings settings) + public static async Task LoadPluginsAsync(PluginsSettings settings) { _metadatas = PluginConfig.Parse(Directories); Settings = settings; Settings.UpdatePluginSettings(_metadatas); - AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); + AllPlugins = await PluginsLoader.PluginsAsync(_metadatas, Settings); } /// diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 0f2e4f996cb..b40f2c5a7cd 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -16,7 +17,16 @@ namespace Flow.Launcher.Core.Plugin { public static class PluginsLoader { - public static List Plugins(List metadatas, PluginsSettings settings) + private static readonly ConcurrentQueue Plugins = new(); + + private static readonly ConcurrentQueue ErroredPlugins = new(); + + public static void AddPlugin(PluginPair plugin) + { + Plugins.Enqueue(plugin); + } + + public static async Task> PluginsAsync(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); @@ -46,17 +56,34 @@ public static List Plugins(List metadatas, PluginsSe .Concat(executablePlugins) .Concat(executableV2Plugins) .ToList(); - return plugins; + + await Task.WhenAll(plugins); + + if (!ErroredPlugins.IsEmpty) + { + var errorPluginString = String.Join(Environment.NewLine, ErroredPlugins); + + var errorMessage = "The following " + + (ErroredPlugins.Count > 1 ? "plugins have " : "plugin has ") + + "errored and cannot be loaded:"; + + _ = Task.Run(() => + { + MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + + $"Please refer to the logs for more information", "", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + }); + } + + return Plugins.ToList(); } - public static IEnumerable DotNetPlugins(List source) + public static IEnumerable DotNetPlugins(List source) { - var erroredPlugins = new List(); - - var plugins = new List(); var metadatas = source.Where(o => AllowedLanguage.IsDotNet(o.Language)); - foreach (var metadata in metadatas) + return metadatas.Select(metadata => Task.Run(() => { var milliseconds = Stopwatch.Debug( $"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () => @@ -100,53 +127,42 @@ public static IEnumerable DotNetPlugins(List source) if (plugin == null) { - erroredPlugins.Add(metadata.Name); + ErroredPlugins.Enqueue(metadata.Name); return; } - plugins.Add(new PluginPair { Plugin = plugin, Metadata = metadata }); + Plugins.Enqueue(new PluginPair { Plugin = plugin, Metadata = metadata }); }); metadata.InitTime += milliseconds; - } - - if (erroredPlugins.Count > 0) - { - var errorPluginString = String.Join(Environment.NewLine, erroredPlugins); - - var errorMessage = "The following " - + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") - + "errored and cannot be loaded:"; - - _ = Task.Run(() => - { - MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - $"Please refer to the logs for more information", "", - MessageBoxButtons.OK, MessageBoxIcon.Warning); - }); - } - - return plugins; + })); } - public static IEnumerable ExecutablePlugins(IEnumerable source) + public static IEnumerable ExecutablePlugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => Task.Run(() => { - Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata - }); + Plugins.Enqueue(new PluginPair + { + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), + Metadata = metadata + }); + })); } - public static IEnumerable ExecutableV2Plugins(IEnumerable source) + public static IEnumerable ExecutableV2Plugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => Task.Run(() => { - Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), Metadata = metadata - }); + Plugins.Enqueue(new PluginPair + { + Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), + Metadata = metadata + }); + })); } } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 4d1adc6cd51..dafbab461d2 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -74,7 +74,7 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => InternationalizationManager.Instance.Settings = _settings; InternationalizationManager.Instance.ChangeLanguage(_settings.Language); - PluginManager.LoadPlugins(_settings.PluginSettings); + await PluginManager.LoadPluginsAsync(_settings.PluginSettings); _mainVM = new MainViewModel(_settings); API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);