Skip to content

Commit be966b7

Browse files
authored
Merge pull request #3272 from Jack251970/plugin_settings_cache_path
Improve Plugin Metadata & Path Management
2 parents 9005c93 + e147a16 commit be966b7

File tree

32 files changed

+449
-186
lines changed

32 files changed

+449
-186
lines changed

Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Flow.Launcher.Plugin.SharedCommands;
55
using System;
66
using System.Collections.Generic;
7+
using System.IO;
78
using System.Linq;
89
using System.Windows;
910
using System.Windows.Forms;
@@ -116,7 +117,10 @@ private IEnumerable<PluginPair> SetPathForPluginPairs(string filePath, string la
116117
foreach (var metadata in PluginMetadataList)
117118
{
118119
if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase))
120+
{
121+
metadata.AssemblyName = string.Empty;
119122
pluginPairs.Add(CreatePluginPair(filePath, metadata));
123+
}
120124
}
121125

122126
return pluginPairs;

Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,16 @@
11
using Flow.Launcher.Core.Resource;
2-
using Flow.Launcher.Infrastructure;
32
using System;
43
using System.Collections.Generic;
54
using System.Diagnostics;
65
using System.IO;
7-
using System.Linq;
86
using System.Text;
97
using System.Text.Json;
108
using System.Threading;
119
using System.Threading.Tasks;
1210
using Flow.Launcher.Infrastructure.Logger;
13-
using Flow.Launcher.Infrastructure.UserSettings;
1411
using Flow.Launcher.Plugin;
1512
using Microsoft.IO;
1613
using System.Windows;
17-
using System.Windows.Controls;
18-
using YamlDotNet.Serialization;
19-
using YamlDotNet.Serialization.NamingConventions;
20-
using CheckBox = System.Windows.Controls.CheckBox;
21-
using Control = System.Windows.Controls.Control;
22-
using Orientation = System.Windows.Controls.Orientation;
23-
using TextBox = System.Windows.Controls.TextBox;
24-
using UserControl = System.Windows.Controls.UserControl;
25-
using System.Windows.Documents;
2614

2715
namespace Flow.Launcher.Core.Plugin
2816
{
@@ -42,7 +30,7 @@ internal abstract class JsonRPCPlugin : JsonRPCPluginBase
4230
private int RequestId { get; set; }
4331

4432
private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
45-
private string SettingPath => Path.Combine(DataLocation.PluginSettingsDirectory, Context.CurrentPluginMetadata.Name, "Settings.json");
33+
private string SettingPath => Path.Combine(Context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "Settings.json");
4634

4735
public override List<Result> LoadContextMenus(Result selectedResult)
4836
{

Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,15 @@
11
using Flow.Launcher.Core.Resource;
2-
using Flow.Launcher.Infrastructure;
32
using System;
43
using System.Collections.Generic;
5-
using System.Diagnostics;
64
using System.IO;
75
using System.Linq;
8-
using System.Text;
96
using System.Text.Json;
107
using System.Threading;
118
using System.Threading.Tasks;
12-
using Flow.Launcher.Infrastructure.Logger;
13-
using Flow.Launcher.Infrastructure.UserSettings;
149
using Flow.Launcher.Plugin;
15-
using Microsoft.IO;
16-
using System.Windows;
17-
using System.Windows.Controls;
1810
using YamlDotNet.Serialization;
1911
using YamlDotNet.Serialization.NamingConventions;
20-
using CheckBox = System.Windows.Controls.CheckBox;
2112
using Control = System.Windows.Controls.Control;
22-
using Orientation = System.Windows.Controls.Orientation;
23-
using TextBox = System.Windows.Controls.TextBox;
24-
using UserControl = System.Windows.Controls.UserControl;
25-
using System.Windows.Documents;
26-
using static System.Windows.Forms.LinkLabel;
27-
using Droplex;
28-
using System.Windows.Forms;
29-
using Microsoft.VisualStudio.Threading;
3013

3114
namespace Flow.Launcher.Core.Plugin
3215
{
@@ -44,8 +27,7 @@ public abstract class JsonRPCPluginBase : IAsyncPlugin, IContextMenu, ISettingPr
4427
private string SettingConfigurationPath =>
4528
Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
4629

47-
private string SettingDirectory => Path.Combine(DataLocation.PluginSettingsDirectory,
48-
Context.CurrentPluginMetadata.Name);
30+
private string SettingDirectory => Context.CurrentPluginMetadata.PluginSettingsDirectoryPath;
4931

5032
private string SettingPath => Path.Combine(SettingDirectory, "Settings.json");
5133

@@ -166,13 +148,5 @@ public Control CreateSettingPanel()
166148
{
167149
return Settings.CreateSettingPanel();
168150
}
169-
170-
public void DeletePluginSettingsDirectory()
171-
{
172-
if (Directory.Exists(SettingDirectory))
173-
{
174-
Directory.Delete(SettingDirectory, true);
175-
}
176-
}
177151
}
178152
}

Flow.Launcher.Core/Plugin/PluginConfig.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.IO;
@@ -9,7 +9,6 @@
99

1010
namespace Flow.Launcher.Core.Plugin
1111
{
12-
1312
internal abstract class PluginConfig
1413
{
1514
/// <summary>
@@ -112,7 +111,7 @@ private static PluginMetadata GetPluginMetadata(string pluginDirectory)
112111
metadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(configPath));
113112
metadata.PluginDirectory = pluginDirectory;
114113
// for plugins which doesn't has ActionKeywords key
115-
metadata.ActionKeywords = metadata.ActionKeywords ?? new List<string> { metadata.ActionKeyword };
114+
metadata.ActionKeywords ??= new List<string> { metadata.ActionKeyword };
116115
// for plugin still use old ActionKeyword
117116
metadata.ActionKeyword = metadata.ActionKeywords?[0];
118117
}
@@ -137,4 +136,4 @@ private static PluginMetadata GetPluginMetadata(string pluginDirectory)
137136
return metadata;
138137
}
139138
}
140-
}
139+
}

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 77 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)