@@ -35,7 +35,7 @@ public static class PluginManager
3535
3636 private static PluginsSettings Settings ;
3737 private static List < PluginMetadata > _metadatas ;
38- private static List < string > _modifiedPlugins = new List < string > ( ) ;
38+ private static List < string > _modifiedPlugins = new ( ) ;
3939
4040 /// <summary>
4141 /// Directories that will hold Flow Launcher plugin directory
@@ -72,15 +72,20 @@ public static async ValueTask DisposePluginsAsync()
7272 {
7373 foreach ( var pluginPair in AllPlugins )
7474 {
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 ;
8489 }
8590 }
8691
@@ -155,6 +160,25 @@ public static void LoadPlugins(PluginsSettings settings)
155160 Settings = settings ;
156161 Settings . UpdatePluginSettings ( _metadatas ) ;
157162 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+ }
158182 }
159183
160184 /// <summary>
@@ -225,10 +249,9 @@ public static ICollection<PluginPair> ValidPluginsForQuery(Query query)
225249 if ( query is null )
226250 return Array . Empty < PluginPair > ( ) ;
227251
228- if ( ! NonGlobalPlugins . ContainsKey ( query . ActionKeyword ) )
252+ if ( ! NonGlobalPlugins . TryGetValue ( query . ActionKeyword , out var plugin ) )
229253 return GlobalPlugins ;
230254
231- var plugin = NonGlobalPlugins [ query . ActionKeyword ] ;
232255 return new List < PluginPair >
233256 {
234257 plugin
@@ -442,10 +465,10 @@ public static bool PluginModified(string uuid)
442465 /// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
443466 /// unless it's a local path installation
444467 /// </summary>
445- public static void UpdatePlugin ( PluginMetadata existingVersion , UserPlugin newVersion , string zipFilePath )
468+ public static async Task UpdatePluginAsync ( PluginMetadata existingVersion , UserPlugin newVersion , string zipFilePath )
446469 {
447470 InstallPlugin ( newVersion , zipFilePath , checkModified : false ) ;
448- UninstallPlugin ( existingVersion , removePluginFromSettings : false , removePluginSettings : false , checkModified : false ) ;
471+ await UninstallPluginAsync ( existingVersion , removePluginFromSettings : false , removePluginSettings : false , checkModified : false ) ;
449472 _modifiedPlugins . Add ( existingVersion . ID ) ;
450473 }
451474
@@ -460,9 +483,9 @@ public static void InstallPlugin(UserPlugin plugin, string zipFilePath)
460483 /// <summary>
461484 /// Uninstall a plugin.
462485 /// </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 )
464487 {
465- UninstallPlugin ( plugin , removePluginFromSettings , removePluginSettings , true ) ;
488+ await UninstallPluginAsync ( plugin , removePluginFromSettings , removePluginSettings , true ) ;
466489 }
467490
468491 #endregion
@@ -543,63 +566,62 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c
543566 }
544567 }
545568
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 )
547570 {
548571 if ( checkModified && PluginModified ( plugin . ID ) )
549572 {
550573 throw new ArgumentException ( $ "Plugin { plugin . Name } has been modified") ;
551574 }
552575
553- if ( removePluginSettings )
576+ if ( removePluginSettings || removePluginFromSettings )
554577 {
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 )
556583 {
557- var assemblyLoader = new PluginAssemblyLoader ( plugin . ExecuteFilePath ) ;
558- var assembly = assemblyLoader . LoadAssemblyAndDependencies ( ) ;
559- var assemblyName = assembly . GetName ( ) . Name ;
584+ await DisposePluginAsync ( pluginPair ) ;
585+ }
586+ }
560587
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+ {
563593 var method = API . GetType ( ) . GetMethod ( "RemovePluginSettings" ) ;
564- var pluginJsonStorage = method ? . Invoke ( API , new object [ ] { assemblyName } ) ;
594+ method ? . Invoke ( API , new object [ ] { plugin . AssemblyName } ) ;
595+ }
565596
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 ) ;
581602 }
582- else // the plugin with json prc interface
603+ catch ( Exception e )
583604 {
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 ) ) ;
598608 }
599609 }
600610
601611 if ( removePluginFromSettings )
602612 {
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+ }
603625 Settings . Plugins . Remove ( plugin . ID ) ;
604626 AllPlugins . RemoveAll ( p => p . Metadata . ID == plugin . ID ) ;
605627 }
0 commit comments