@@ -35,7 +35,7 @@ public static class PluginManager
35
35
36
36
private static PluginsSettings Settings ;
37
37
private static List < PluginMetadata > _metadatas ;
38
- private static List < string > _modifiedPlugins = new List < string > ( ) ;
38
+ private static List < string > _modifiedPlugins = new ( ) ;
39
39
40
40
/// <summary>
41
41
/// Directories that will hold Flow Launcher plugin directory
@@ -72,15 +72,20 @@ public static async ValueTask DisposePluginsAsync()
72
72
{
73
73
foreach ( var pluginPair in AllPlugins )
74
74
{
75
- switch ( pluginPair . Plugin )
76
- {
77
- case IDisposable disposable :
78
- disposable . Dispose ( ) ;
79
- break ;
80
- case IAsyncDisposable asyncDisposable :
81
- await asyncDisposable . DisposeAsync ( ) ;
82
- break ;
83
- }
75
+ await DisposePluginAsync ( pluginPair ) ;
76
+ }
77
+ }
78
+
79
+ private static async Task DisposePluginAsync ( PluginPair pluginPair )
80
+ {
81
+ switch ( pluginPair . Plugin )
82
+ {
83
+ case IDisposable disposable :
84
+ disposable . Dispose ( ) ;
85
+ break ;
86
+ case IAsyncDisposable asyncDisposable :
87
+ await asyncDisposable . DisposeAsync ( ) ;
88
+ break ;
84
89
}
85
90
}
86
91
@@ -155,6 +160,25 @@ public static void LoadPlugins(PluginsSettings settings)
155
160
Settings = settings ;
156
161
Settings . UpdatePluginSettings ( _metadatas ) ;
157
162
AllPlugins = PluginsLoader . Plugins ( _metadatas , Settings ) ;
163
+ // Since dotnet plugins need to get assembly name first, we should update plugin directory after loading plugins
164
+ UpdatePluginDirectory ( _metadatas ) ;
165
+ }
166
+
167
+ private static void UpdatePluginDirectory ( List < PluginMetadata > metadatas )
168
+ {
169
+ foreach ( var metadata in metadatas )
170
+ {
171
+ if ( AllowedLanguage . IsDotNet ( metadata . Language ) )
172
+ {
173
+ metadata . PluginSettingsDirectoryPath = Path . Combine ( DataLocation . PluginSettingsDirectory , metadata . AssemblyName ) ;
174
+ metadata . PluginCacheDirectoryPath = Path . Combine ( DataLocation . PluginCacheDirectory , metadata . AssemblyName ) ;
175
+ }
176
+ else
177
+ {
178
+ metadata . PluginSettingsDirectoryPath = Path . Combine ( DataLocation . PluginSettingsDirectory , metadata . Name ) ;
179
+ metadata . PluginCacheDirectoryPath = Path . Combine ( DataLocation . PluginCacheDirectory , metadata . Name ) ;
180
+ }
181
+ }
158
182
}
159
183
160
184
/// <summary>
@@ -225,10 +249,9 @@ public static ICollection<PluginPair> ValidPluginsForQuery(Query query)
225
249
if ( query is null )
226
250
return Array . Empty < PluginPair > ( ) ;
227
251
228
- if ( ! NonGlobalPlugins . ContainsKey ( query . ActionKeyword ) )
252
+ if ( ! NonGlobalPlugins . TryGetValue ( query . ActionKeyword , out var plugin ) )
229
253
return GlobalPlugins ;
230
254
231
- var plugin = NonGlobalPlugins [ query . ActionKeyword ] ;
232
255
return new List < PluginPair >
233
256
{
234
257
plugin
@@ -442,10 +465,10 @@ public static bool PluginModified(string uuid)
442
465
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
443
466
/// unless it's a local path installation
444
467
/// </summary>
445
- public static void UpdatePlugin ( PluginMetadata existingVersion , UserPlugin newVersion , string zipFilePath )
468
+ public static async Task UpdatePluginAsync ( PluginMetadata existingVersion , UserPlugin newVersion , string zipFilePath )
446
469
{
447
470
InstallPlugin ( newVersion , zipFilePath , checkModified : false ) ;
448
- UninstallPlugin ( existingVersion , removePluginFromSettings : false , removePluginSettings : false , checkModified : false ) ;
471
+ await UninstallPluginAsync ( existingVersion , removePluginFromSettings : false , removePluginSettings : false , checkModified : false ) ;
449
472
_modifiedPlugins . Add ( existingVersion . ID ) ;
450
473
}
451
474
@@ -460,9 +483,9 @@ public static void InstallPlugin(UserPlugin plugin, string zipFilePath)
460
483
/// <summary>
461
484
/// Uninstall a plugin.
462
485
/// </summary>
463
- public static void UninstallPlugin ( PluginMetadata plugin , bool removePluginFromSettings = true , bool removePluginSettings = false )
486
+ public static async Task UninstallPluginAsync ( PluginMetadata plugin , bool removePluginFromSettings = true , bool removePluginSettings = false )
464
487
{
465
- UninstallPlugin ( plugin , removePluginFromSettings , removePluginSettings , true ) ;
488
+ await UninstallPluginAsync ( plugin , removePluginFromSettings , removePluginSettings , true ) ;
466
489
}
467
490
468
491
#endregion
@@ -543,63 +566,62 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c
543
566
}
544
567
}
545
568
546
- internal static void UninstallPlugin ( PluginMetadata plugin , bool removePluginFromSettings , bool removePluginSettings , bool checkModified )
569
+ internal static async Task UninstallPluginAsync ( PluginMetadata plugin , bool removePluginFromSettings , bool removePluginSettings , bool checkModified )
547
570
{
548
571
if ( checkModified && PluginModified ( plugin . ID ) )
549
572
{
550
573
throw new ArgumentException ( $ "Plugin { plugin . Name } has been modified") ;
551
574
}
552
575
553
- if ( removePluginSettings )
576
+ if ( removePluginSettings || removePluginFromSettings )
554
577
{
555
- if ( AllowedLanguage . IsDotNet ( plugin . Language ) ) // for the plugin in .NET, we can use assembly loader
578
+ // If we want to remove plugin from AllPlugins,
579
+ // we need to dispose them so that they can release file handles
580
+ // which can help FL to delete the plugin settings & cache folders successfully
581
+ var pluginPairs = AllPlugins . FindAll ( p => p . Metadata . ID == plugin . ID ) ;
582
+ foreach ( var pluginPair in pluginPairs )
556
583
{
557
- var assemblyLoader = new PluginAssemblyLoader ( plugin . ExecuteFilePath ) ;
558
- var assembly = assemblyLoader . LoadAssemblyAndDependencies ( ) ;
559
- var assemblyName = assembly . GetName ( ) . Name ;
584
+ await DisposePluginAsync ( pluginPair ) ;
585
+ }
586
+ }
560
587
561
- // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin
562
- // so we need to remove it from the api instance
588
+ if ( removePluginSettings )
589
+ {
590
+ // For dotnet plugins, we need to remove their PluginJsonStorage instance
591
+ if ( AllowedLanguage . IsDotNet ( plugin . Language ) )
592
+ {
563
593
var method = API . GetType ( ) . GetMethod ( "RemovePluginSettings" ) ;
564
- var pluginJsonStorage = method ? . Invoke ( API , new object [ ] { assemblyName } ) ;
594
+ method ? . Invoke ( API , new object [ ] { plugin . AssemblyName } ) ;
595
+ }
565
596
566
- // if there exists a json storage for current plugin, we need to delete the directory path
567
- if ( pluginJsonStorage != null )
568
- {
569
- var deleteMethod = pluginJsonStorage . GetType ( ) . GetMethod ( "DeleteDirectory" ) ;
570
- try
571
- {
572
- deleteMethod ? . Invoke ( pluginJsonStorage , null ) ;
573
- }
574
- catch ( Exception e )
575
- {
576
- Log . Exception ( $ "|PluginManager.UninstallPlugin|Failed to delete plugin json folder for { plugin . Name } ", e ) ;
577
- API . ShowMsg ( API . GetTranslation ( "failedToRemovePluginSettingsTitle" ) ,
578
- string . Format ( API . GetTranslation ( "failedToRemovePluginSettingsMessage" ) , plugin . Name ) ) ;
579
- }
580
- }
597
+ try
598
+ {
599
+ var pluginSettingsDirectory = plugin . PluginSettingsDirectoryPath ;
600
+ if ( Directory . Exists ( pluginSettingsDirectory ) )
601
+ Directory . Delete ( pluginSettingsDirectory , true ) ;
581
602
}
582
- else // the plugin with json prc interface
603
+ catch ( Exception e )
583
604
{
584
- var pluginPair = AllPlugins . FirstOrDefault ( p => p . Metadata . ID == plugin . ID ) ;
585
- if ( pluginPair != null && pluginPair . Plugin is JsonRPCPlugin jsonRpcPlugin )
586
- {
587
- try
588
- {
589
- jsonRpcPlugin . DeletePluginSettingsDirectory ( ) ;
590
- }
591
- catch ( Exception e )
592
- {
593
- Log . Exception ( $ "|PluginManager.UninstallPlugin|Failed to delete plugin json folder for { plugin . Name } ", e ) ;
594
- API . ShowMsg ( API . GetTranslation ( "failedToRemovePluginSettingsTitle" ) ,
595
- string . Format ( API . GetTranslation ( "failedToRemovePluginSettingsMessage" ) , plugin . Name ) ) ;
596
- }
597
- }
605
+ Log . Exception ( $ "|PluginManager.UninstallPlugin|Failed to delete plugin settings folder for { plugin . Name } ", e ) ;
606
+ API . ShowMsg ( API . GetTranslation ( "failedToRemovePluginSettingsTitle" ) ,
607
+ string . Format ( API . GetTranslation ( "failedToRemovePluginSettingsMessage" ) , plugin . Name ) ) ;
598
608
}
599
609
}
600
610
601
611
if ( removePluginFromSettings )
602
612
{
613
+ try
614
+ {
615
+ var pluginCacheDirectory = plugin . PluginCacheDirectoryPath ;
616
+ if ( Directory . Exists ( pluginCacheDirectory ) )
617
+ Directory . Delete ( pluginCacheDirectory , true ) ;
618
+ }
619
+ catch ( Exception e )
620
+ {
621
+ Log . Exception ( $ "|PluginManager.UninstallPlugin|Failed to delete plugin cache folder for { plugin . Name } ", e ) ;
622
+ API . ShowMsg ( API . GetTranslation ( "failedToRemovePluginCacheTitle" ) ,
623
+ string . Format ( API . GetTranslation ( "failedToRemovePluginCacheMessage" ) , plugin . Name ) ) ;
624
+ }
603
625
Settings . Plugins . Remove ( plugin . ID ) ;
604
626
AllPlugins . RemoveAll ( p => p . Metadata . ID == plugin . ID ) ;
605
627
}
0 commit comments