Skip to content

Commit a721bd7

Browse files
committed
Merge remote-tracking branch 'upstream/dev' into PluginAPIExpand
# Conflicts: # Flow.Launcher.Plugin/IPublicAPI.cs
2 parents 4f5b2d3 + fc78c0c commit a721bd7

File tree

73 files changed

+1654
-817
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1654
-817
lines changed

Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal PluginAssemblyLoader(string assemblyFilePath)
2020
dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath);
2121
assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath));
2222

23-
referencedPluginPackageDependencyResolver =
23+
referencedPluginPackageDependencyResolver =
2424
new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.Plugin.dll"));
2525
}
2626

@@ -38,15 +38,15 @@ protected override Assembly Load(AssemblyName assemblyName)
3838
// that use Newtonsoft.Json
3939
if (assemblyPath == null || ExistsInReferencedPluginPackage(assemblyName))
4040
return null;
41-
41+
4242
return LoadFromAssemblyPath(assemblyPath);
4343
}
4444

45-
internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type)
45+
internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, params Type[] types)
4646
{
4747
var allTypes = assembly.ExportedTypes;
4848

49-
return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(type));
49+
return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Intersect(types).Any());
5050
}
5151

5252
internal bool ExistsInReferencedPluginPackage(AssemblyName assemblyName)

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.IO;
55
using System.Linq;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using Flow.Launcher.Infrastructure;
89
using Flow.Launcher.Infrastructure.Logger;
@@ -52,13 +53,14 @@ public static void Save()
5253
}
5354
}
5455

55-
public static void ReloadData()
56+
public static async Task ReloadData()
5657
{
57-
foreach(var plugin in AllPlugins)
58+
await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch
5859
{
59-
var reloadablePlugin = plugin.Plugin as IReloadable;
60-
reloadablePlugin?.ReloadData();
61-
}
60+
IReloadable p => Task.Run(p.ReloadData),
61+
IAsyncReloadable p => p.ReloadDataAsync(),
62+
_ => Task.CompletedTask,
63+
}).ToArray());
6264
}
6365

6466
static PluginManager()
@@ -86,50 +88,62 @@ public static void LoadPlugins(PluginsSettings settings)
8688
/// Call initialize for all plugins
8789
/// </summary>
8890
/// <returns>return the list of failed to init plugins or null for none</returns>
89-
public static void InitializePlugins(IPublicAPI api)
91+
public static async Task InitializePlugins(IPublicAPI api)
9092
{
9193
API = api;
9294
var failedPlugins = new ConcurrentQueue<PluginPair>();
93-
Parallel.ForEach(AllPlugins, pair =>
95+
96+
var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate
9497
{
9598
try
9699
{
97-
var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () =>
100+
var milliseconds = pair.Plugin switch
98101
{
99-
pair.Plugin.Init(new PluginInitContext
100-
{
101-
CurrentPluginMetadata = pair.Metadata,
102-
API = API
103-
});
104-
});
102+
IAsyncPlugin plugin
103+
=> await Stopwatch.DebugAsync($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>",
104+
() => plugin.InitAsync(new PluginInitContext(pair.Metadata, API))),
105+
IPlugin plugin
106+
=> Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>",
107+
() => plugin.Init(new PluginInitContext(pair.Metadata, API))),
108+
_ => throw new ArgumentException(),
109+
};
105110
pair.Metadata.InitTime += milliseconds;
106-
Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
111+
Log.Info(
112+
$"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
107113
}
108114
catch (Exception e)
109115
{
110116
Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e);
111-
pair.Metadata.Disabled = true;
117+
pair.Metadata.Disabled = true;
112118
failedPlugins.Enqueue(pair);
113119
}
114-
});
120+
}));
121+
122+
await Task.WhenAll(InitTasks);
115123

116124
_contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
117125
foreach (var plugin in AllPlugins)
118126
{
119-
if (IsGlobalPlugin(plugin.Metadata))
120-
GlobalPlugins.Add(plugin);
121-
122-
// Plugins may have multiple ActionKeywords, eg. WebSearch
123-
plugin.Metadata.ActionKeywords
124-
.Where(x => x != Query.GlobalPluginWildcardSign)
125-
.ToList()
126-
.ForEach(x => NonGlobalPlugins[x] = plugin);
127+
foreach (var actionKeyword in plugin.Metadata.ActionKeywords)
128+
{
129+
switch (actionKeyword)
130+
{
131+
case Query.GlobalPluginWildcardSign:
132+
GlobalPlugins.Add(plugin);
133+
break;
134+
default:
135+
NonGlobalPlugins[actionKeyword] = plugin;
136+
break;
137+
}
138+
}
127139
}
128140

129141
if (failedPlugins.Any())
130142
{
131143
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
132-
API.ShowMsg($"Fail to Init Plugins", $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", "", false);
144+
API.ShowMsg($"Fail to Init Plugins",
145+
$"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help",
146+
"", false);
133147
}
134148
}
135149

@@ -146,24 +160,46 @@ public static List<PluginPair> ValidPluginsForQuery(Query query)
146160
}
147161
}
148162

149-
public static List<Result> QueryForPlugin(PluginPair pair, Query query)
163+
public static async Task<List<Result>> QueryForPlugin(PluginPair pair, Query query, CancellationToken token)
150164
{
151165
var results = new List<Result>();
152166
try
153167
{
154168
var metadata = pair.Metadata;
155-
var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () =>
169+
170+
long milliseconds = -1L;
171+
172+
switch (pair.Plugin)
156173
{
157-
results = pair.Plugin.Query(query) ?? new List<Result>();
158-
UpdatePluginMetadata(results, metadata, query);
159-
});
174+
case IAsyncPlugin plugin:
175+
milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}",
176+
async () => results = await plugin.QueryAsync(query, token).ConfigureAwait(false));
177+
break;
178+
case IPlugin plugin:
179+
await Task.Run(() => milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}",
180+
() => results = plugin.Query(query)), token).ConfigureAwait(false);
181+
break;
182+
default:
183+
throw new ArgumentOutOfRangeException();
184+
}
185+
token.ThrowIfCancellationRequested();
186+
UpdatePluginMetadata(results, metadata, query);
187+
160188
metadata.QueryCount += 1;
161-
metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
189+
metadata.AvgQueryTime =
190+
metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
191+
token.ThrowIfCancellationRequested();
192+
}
193+
catch (OperationCanceledException)
194+
{
195+
// null will be fine since the results will only be added into queue if the token hasn't been cancelled
196+
return results = null;
162197
}
163198
catch (Exception e)
164199
{
165200
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
166201
}
202+
167203
return results;
168204
}
169205

@@ -182,11 +218,6 @@ public static void UpdatePluginMetadata(List<Result> results, PluginMetadata met
182218
}
183219
}
184220

185-
private static bool IsGlobalPlugin(PluginMetadata metadata)
186-
{
187-
return metadata.ActionKeywords.Contains(Query.GlobalPluginWildcardSign);
188-
}
189-
190221
/// <summary>
191222
/// get specified plugin, return null if not found
192223
/// </summary>
@@ -222,16 +253,19 @@ public static List<Result> GetContextMenusForPlugin(Result result)
222253
}
223254
catch (Exception e)
224255
{
225-
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e);
256+
Log.Exception(
257+
$"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
258+
e);
226259
}
227260
}
261+
228262
return results;
229263
}
230264

231265
public static bool ActionKeywordRegistered(string actionKeyword)
232266
{
233267
return actionKeyword != Query.GlobalPluginWildcardSign
234-
&& NonGlobalPlugins.ContainsKey(actionKeyword);
268+
&& NonGlobalPlugins.ContainsKey(actionKeyword);
235269
}
236270

237271
/// <summary>
@@ -249,6 +283,7 @@ public static void AddActionKeyword(string id, string newActionKeyword)
249283
{
250284
NonGlobalPlugins[newActionKeyword] = plugin;
251285
}
286+
252287
plugin.Metadata.ActionKeywords.Add(newActionKeyword);
253288
}
254289

@@ -262,16 +297,16 @@ public static void RemoveActionKeyword(string id, string oldActionkeyword)
262297
if (oldActionkeyword == Query.GlobalPluginWildcardSign
263298
&& // Plugins may have multiple ActionKeywords that are global, eg. WebSearch
264299
plugin.Metadata.ActionKeywords
265-
.Where(x => x == Query.GlobalPluginWildcardSign)
266-
.ToList()
267-
.Count == 1)
300+
.Where(x => x == Query.GlobalPluginWildcardSign)
301+
.ToList()
302+
.Count == 1)
268303
{
269304
GlobalPlugins.Remove(plugin);
270305
}
271-
306+
272307
if (oldActionkeyword != Query.GlobalPluginWildcardSign)
273308
NonGlobalPlugins.Remove(oldActionkeyword);
274-
309+
275310

276311
plugin.Metadata.ActionKeywords.Remove(oldActionkeyword);
277312
}

Flow.Launcher.Core/Plugin/PluginsLoader.cs

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -37,56 +37,59 @@ public static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
3737

3838
foreach (var metadata in metadatas)
3939
{
40-
var milliseconds = Stopwatch.Debug($"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () =>
41-
{
42-
43-
#if DEBUG
44-
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
45-
var assembly = assemblyLoader.LoadAssemblyAndDependencies();
46-
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin));
47-
var plugin = (IPlugin)Activator.CreateInstance(type);
48-
#else
49-
Assembly assembly = null;
50-
IPlugin plugin = null;
51-
52-
try
40+
var milliseconds = Stopwatch.Debug(
41+
$"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () =>
5342
{
43+
#if DEBUG
5444
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
55-
assembly = assemblyLoader.LoadAssemblyAndDependencies();
56-
57-
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin));
58-
59-
plugin = (IPlugin)Activator.CreateInstance(type);
60-
}
61-
catch (Exception e) when (assembly == null)
62-
{
63-
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
64-
}
65-
catch (InvalidOperationException e)
66-
{
67-
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
68-
}
69-
catch (ReflectionTypeLoadException e)
70-
{
71-
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
72-
}
73-
catch (Exception e)
74-
{
75-
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
76-
}
45+
var assembly = assemblyLoader.LoadAssemblyAndDependencies();
46+
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin),
47+
typeof(IAsyncPlugin));
7748

78-
if (plugin == null)
79-
{
80-
erroredPlugins.Add(metadata.Name);
81-
return;
82-
}
49+
var plugin = Activator.CreateInstance(type);
50+
#else
51+
Assembly assembly = null;
52+
object plugin = null;
53+
54+
try
55+
{
56+
var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath);
57+
assembly = assemblyLoader.LoadAssemblyAndDependencies();
58+
59+
var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin),
60+
typeof(IAsyncPlugin));
61+
62+
plugin = Activator.CreateInstance(type);
63+
}
64+
catch (Exception e) when (assembly == null)
65+
{
66+
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
67+
}
68+
catch (InvalidOperationException e)
69+
{
70+
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
71+
}
72+
catch (ReflectionTypeLoadException e)
73+
{
74+
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
75+
}
76+
catch (Exception e)
77+
{
78+
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
79+
}
80+
81+
if (plugin == null)
82+
{
83+
erroredPlugins.Add(metadata.Name);
84+
return;
85+
}
8386
#endif
84-
plugins.Add(new PluginPair
85-
{
86-
Plugin = plugin,
87-
Metadata = metadata
87+
plugins.Add(new PluginPair
88+
{
89+
Plugin = plugin,
90+
Metadata = metadata
91+
});
8892
});
89-
});
9093
metadata.InitTime += milliseconds;
9194
}
9295

@@ -95,15 +98,15 @@ public static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
9598
var errorPluginString = String.Join(Environment.NewLine, erroredPlugins);
9699

97100
var errorMessage = "The following "
98-
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
99-
+ "errored and cannot be loaded:";
101+
+ (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ")
102+
+ "errored and cannot be loaded:";
100103

101104
Task.Run(() =>
102105
{
103106
MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
104-
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
105-
$"Please refer to the logs for more information","",
106-
MessageBoxButtons.OK, MessageBoxIcon.Warning);
107+
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
108+
$"Please refer to the logs for more information", "",
109+
MessageBoxButtons.OK, MessageBoxIcon.Warning);
107110
});
108111
}
109112

@@ -179,6 +182,5 @@ public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetada
179182
Metadata = metadata
180183
});
181184
}
182-
183185
}
184-
}
186+
}

0 commit comments

Comments
 (0)