From 89bca3b6a96976abf7acc98d2fea4176f6bf24bc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:00:57 +0800 Subject: [PATCH 01/27] Add plugin cache path --- Flow.Launcher.Infrastructure/Constant.cs | 1 + Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index c86ed432467..d694e0d3595 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -47,6 +47,7 @@ public static class Constant public const string Themes = "Themes"; public const string Settings = "Settings"; public const string Logs = "Logs"; + public const string Cache = "Cache"; public const string Website = "https://flowlauncher.com"; public const string SponsorPage = "https://github.com/sponsors/Flow-Launcher"; diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index e294f52b8c5..fe3be43aaf5 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -26,7 +26,8 @@ public static bool PortableDataLocationInUse() } public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); - public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), "Settings", Constant.Plugins); + public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), Constant.Settings, Constant.Plugins); + public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); public const string PythonEnvironmentName = "Python"; public const string NodeEnvironmentName = "Node.js"; From 591941898048af3e07a394bc17d3d780758a1dc9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:01:53 +0800 Subject: [PATCH 02/27] Add assembly name & plugin settings path & plugin cache path in meta data for csharp plugins --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 6 ++++++ Flow.Launcher.Plugin/PluginMetadata.cs | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 7973c66baa7..e35f2a09732 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -73,6 +74,11 @@ public static IEnumerable DotNetPlugins(List source) typeof(IAsyncPlugin)); plugin = Activator.CreateInstance(type) as IAsyncPlugin; + + var assemblyName = assembly.GetName().Name; + metadata.AssemblyName = assemblyName; + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, assemblyName); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, assemblyName); } #if DEBUG catch (Exception e) diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index b4e06913e32..15ba2deaa77 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -16,12 +16,14 @@ public class PluginMetadata : BaseModel public string Website { get; set; } public bool Disabled { get; set; } public string ExecuteFilePath { get; private set;} - public string ExecuteFileName { get; set; } + [JsonIgnore] + public string AssemblyName { get; internal set; } + public string PluginDirectory { - get { return _pluginDirectory; } + get => _pluginDirectory; internal set { _pluginDirectory = value; @@ -49,9 +51,17 @@ public override string ToString() /// [JsonIgnore] public long InitTime { get; set; } + [JsonIgnore] public long AvgQueryTime { get; set; } + [JsonIgnore] public int QueryCount { get; set; } + + [JsonIgnore] + public string PluginSettingsDirectoryPath { get; internal set; } + + [JsonIgnore] + public string PluginCacheDirectoryPath { get; internal set; } } } From f1b5e68cf2efc1da7b561dc495c1b89954196317 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:02:32 +0800 Subject: [PATCH 03/27] Remove reflection codes for deleting csharp plugin settings --- Flow.Launcher.Core/Plugin/PluginManager.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 29f1604c4b1..7f73bf1cf18 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -548,14 +548,10 @@ internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFro { if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader { - var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath); - var assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var assemblyName = assembly.GetName().Name; - // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin // so we need to remove it from the api instance var method = API.GetType().GetMethod("RemovePluginSettings"); - var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName }); + var pluginJsonStorage = method?.Invoke(API, new object[] { plugin.AssemblyName }); // if there exists a json storage for current plugin, we need to delete the directory path if (pluginJsonStorage != null) From 1aaba46b8f284ce512ab8aa6c98ee66f97511927 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:06:21 +0800 Subject: [PATCH 04/27] Use constants & data location for code quality --- Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs | 2 +- Flow.Launcher.Infrastructure/Storage/JsonStorage.cs | 2 +- Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs | 2 +- Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 4 +++- .../SettingPages/ViewModels/SettingsPaneAboutViewModel.cs | 4 ++-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 2a439b8cce5..48c5fc1419b 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Infrastructure.Storage /// public class BinaryStorage { - const string DirectoryName = "Cache"; + const string DirectoryName = Constant.Cache; const string FileSuffix = ".cache"; diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 64225062785..7008026c8df 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -16,7 +16,7 @@ namespace Flow.Launcher.Infrastructure.Storage protected T? Data; // need a new directory name - public const string DirectoryName = "Settings"; + public const string DirectoryName = Constant.Settings; public const string FileSuffix = ".json"; protected string FilePath { get; init; } = null!; diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index bc3900da802..42453a7a136 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -13,7 +13,7 @@ public PluginJsonStorage() // C# related, add python related below var dataType = typeof(T); AssemblyName = dataType.Assembly.GetName().Name; - DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, AssemblyName); + DirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, AssemblyName); Helper.ValidateDirectory(DirectoryPath); FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index fe3be43aaf5..97cd2da54ab 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -25,8 +25,10 @@ public static bool PortableDataLocationInUse() return false; } + public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); - public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), Constant.Settings, Constant.Plugins); + + public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectorty, Constant.Plugins); public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); public const string PythonEnvironmentName = "Python"; diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 6e81db5e00d..2892a065445 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -77,13 +77,13 @@ private void AskClearLogFolderConfirmation() [RelayCommand] private void OpenSettingsFolder() { - PluginManager.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Settings)); + PluginManager.API.OpenDirectory(DataLocation.SettingsDirectorty); } [RelayCommand] private void OpenParentOfSettingsFolder(object parameter) { - string settingsFolderPath = Path.Combine(DataLocation.DataDirectory(), Constant.Settings); + string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectorty); string parentFolderPath = Path.GetDirectoryName(settingsFolderPath); PluginManager.API.OpenDirectory(parentFolderPath); } From 9cd30112fe6d9056a211e8f9dabe17c056e429a9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:28:03 +0800 Subject: [PATCH 05/27] Add documents for plugin metadata --- Flow.Launcher.Plugin/PluginMetadata.cs | 98 ++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 15ba2deaa77..6dc8acc6338 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -4,23 +4,74 @@ namespace Flow.Launcher.Plugin { + /// + /// Plugin metadata + /// public class PluginMetadata : BaseModel { private string _pluginDirectory; + + /// + /// Plugin ID. + /// public string ID { get; set; } + + /// + /// Plugin name. + /// public string Name { get; set; } + + /// + /// Plugin author. + /// public string Author { get; set; } + + /// + /// Plugin version. + /// public string Version { get; set; } + + /// + /// Plugin language. + /// See + /// public string Language { get; set; } + + /// + /// Plugin description. + /// public string Description { get; set; } + + /// + /// Plugin website. + /// public string Website { get; set; } + + /// + /// Whether plugin is disabled. + /// public bool Disabled { get; set; } - public string ExecuteFilePath { get; private set;} + + /// + /// Plugin execute file path. + /// + public string ExecuteFilePath { get; private set; } + + /// + /// Plugin execute file name. + /// public string ExecuteFileName { get; set; } + /// + /// Plugin assembly name. + /// Only available for .Net plugins. + /// [JsonIgnore] public string AssemblyName { get; internal set; } + /// + /// Plugin source directory. + /// public string PluginDirectory { get => _pluginDirectory; @@ -32,36 +83,69 @@ internal set } } + /// + /// The first action keyword of plugin. + /// public string ActionKeyword { get; set; } + /// + /// All action keywords of plugin. + /// public List ActionKeywords { get; set; } + /// + /// Plugin icon path. + /// public string IcoPath { get; set;} - - public override string ToString() - { - return Name; - } + /// + /// Plugin priority. + /// [JsonIgnore] public int Priority { get; set; } /// - /// Init time include both plugin load time and init time + /// Init time include both plugin load time and init time. /// [JsonIgnore] public long InitTime { get; set; } + /// + /// Average query time. + /// [JsonIgnore] public long AvgQueryTime { get; set; } + /// + /// Query count. + /// [JsonIgnore] public int QueryCount { get; set; } + /// + /// The path to the plugin settings directory. + /// It is used to store plugin settings files and data files. + /// When plugin is deleted, FL will ask users whether to keep its settings. + /// If users do not want to keep, this directory will be deleted. + /// [JsonIgnore] public string PluginSettingsDirectoryPath { get; internal set; } + /// + /// The path to the plugin cache directory. + /// It is used to store cache files. + /// When plugin is deleted, this directory will be deleted as well. + /// [JsonIgnore] public string PluginCacheDirectoryPath { get; internal set; } + + /// + /// Convert to string. + /// + /// + public override string ToString() + { + return Name; + } } } From 8f7ad27aef953b7b8d9343b0f1444c596c9aef0c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Feb 2025 21:28:14 +0800 Subject: [PATCH 06/27] Remove useless usings --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 11 ----------- Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs | 16 ---------------- 2 files changed, 27 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 97c3c898121..f6b200aa520 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -1,10 +1,8 @@ using Flow.Launcher.Core.Resource; -using Flow.Launcher.Infrastructure; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Text; using System.Text.Json; using System.Threading; @@ -14,15 +12,6 @@ using Flow.Launcher.Plugin; using Microsoft.IO; using System.Windows; -using System.Windows.Controls; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; -using CheckBox = System.Windows.Controls.CheckBox; -using Control = System.Windows.Controls.Control; -using Orientation = System.Windows.Controls.Orientation; -using TextBox = System.Windows.Controls.TextBox; -using UserControl = System.Windows.Controls.UserControl; -using System.Windows.Documents; namespace Flow.Launcher.Core.Plugin { diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs index ed8f94bcf25..947221e778c 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs @@ -1,32 +1,16 @@ using Flow.Launcher.Core.Resource; -using Flow.Launcher.Infrastructure; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Microsoft.IO; -using System.Windows; -using System.Windows.Controls; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; -using CheckBox = System.Windows.Controls.CheckBox; using Control = System.Windows.Controls.Control; -using Orientation = System.Windows.Controls.Orientation; -using TextBox = System.Windows.Controls.TextBox; -using UserControl = System.Windows.Controls.UserControl; -using System.Windows.Documents; -using static System.Windows.Forms.LinkLabel; -using Droplex; -using System.Windows.Forms; -using Microsoft.VisualStudio.Threading; namespace Flow.Launcher.Core.Plugin { From d07b304f9ec809ab7636151f571dd515e14b540f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 11:35:41 +0800 Subject: [PATCH 07/27] Improve code quality --- Flow.Launcher.Core/Plugin/PluginConfig.cs | 7 +++---- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 4 ++-- .../UserSettings/PluginSettings.cs | 4 +--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index dd6517a7fd1..163f970469d 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.IO; @@ -9,7 +9,6 @@ namespace Flow.Launcher.Core.Plugin { - internal abstract class PluginConfig { /// @@ -112,7 +111,7 @@ private static PluginMetadata GetPluginMetadata(string pluginDirectory) metadata = JsonSerializer.Deserialize(File.ReadAllText(configPath)); metadata.PluginDirectory = pluginDirectory; // for plugins which doesn't has ActionKeywords key - metadata.ActionKeywords = metadata.ActionKeywords ?? new List { metadata.ActionKeyword }; + metadata.ActionKeywords ??= new List { metadata.ActionKeyword }; // for plugin still use old ActionKeyword metadata.ActionKeyword = metadata.ActionKeywords?[0]; } @@ -137,4 +136,4 @@ private static PluginMetadata GetPluginMetadata(string pluginDirectory) return metadata; } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index e35f2a09732..70c43435d32 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -81,7 +81,7 @@ public static IEnumerable DotNetPlugins(List source) metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, assemblyName); } #if DEBUG - catch (Exception e) + catch (Exception) { throw; } @@ -117,7 +117,7 @@ public static IEnumerable DotNetPlugins(List source) if (erroredPlugins.Count > 0) { - var errorPluginString = String.Join(Environment.NewLine, erroredPlugins); + var errorPluginString = string.Join(Environment.NewLine, erroredPlugins); var errorMessage = "The following " + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") diff --git a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs index 98f4dccda18..1d06e18f738 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs @@ -32,10 +32,8 @@ public void UpdatePluginSettings(List metadatas) { foreach (var metadata in metadatas) { - if (Plugins.ContainsKey(metadata.ID)) + if (Plugins.TryGetValue(metadata.ID, out var settings)) { - var settings = Plugins[metadata.ID]; - if (string.IsNullOrEmpty(settings.Version)) settings.Version = metadata.Version; From b50db58673d19756dda1de4d8d453f8c5e535504 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 12:37:08 +0800 Subject: [PATCH 08/27] Add assembly name & plugin settings path & plugin cache path in meta data for non-csharp plugins --- .../Environments/AbstractPluginEnvironment.cs | 6 ++++ Flow.Launcher.Core/Plugin/PluginsLoader.cs | 28 ++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 6d41e23834e..93058759a10 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -4,6 +4,7 @@ using Flow.Launcher.Plugin.SharedCommands; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Windows; using System.Windows.Forms; @@ -113,7 +114,12 @@ private IEnumerable SetPathForPluginPairs(string filePath, string la foreach (var metadata in PluginMetadataList) { if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) + { pluginPairs.Add(CreatePluginPair(filePath, metadata)); + metadata.AssemblyName = string.Empty; + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + } } return pluginPairs; diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 70c43435d32..91177e74545 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -139,9 +139,19 @@ public static IEnumerable ExecutablePlugins(IEnumerable o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => { - Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata + var plugin = new PluginPair + { + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), + Metadata = metadata + }; + + plugin.Metadata.AssemblyName = string.Empty; + plugin.Metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, plugin.Metadata.Name); + plugin.Metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, plugin.Metadata.Name); + + return plugin; }); } @@ -149,9 +159,19 @@ public static IEnumerable ExecutableV2Plugins(IEnumerable o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => { - Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), Metadata = metadata + var plugin = new PluginPair + { + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), + Metadata = metadata + }; + + plugin.Metadata.AssemblyName = string.Empty; + plugin.Metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, plugin.Metadata.Name); + plugin.Metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, plugin.Metadata.Name); + + return plugin; }); } } From 601211173c761255770b53502d025549c5d985df Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:17:02 +0800 Subject: [PATCH 09/27] Add log directory & version log directory & themes directory in data location --- Flow.Launcher.Infrastructure/Logger/Log.cs | 2 +- .../UserSettings/DataLocation.cs | 4 ++++ .../ViewModels/SettingsPaneAboutViewModel.cs | 2 +- .../ViewModels/SettingsPaneThemeViewModel.cs | 2 +- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 14 ++++++-------- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Logger/Log.cs b/Flow.Launcher.Infrastructure/Logger/Log.cs index d4bd473acf6..fff52a8ab72 100644 --- a/Flow.Launcher.Infrastructure/Logger/Log.cs +++ b/Flow.Launcher.Infrastructure/Logger/Log.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Infrastructure.Logger { public static class Log { - public const string DirectoryName = "Logs"; + public const string DirectoryName = Constant.Logs; public static string CurrentLogDirectory { get; } diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 97cd2da54ab..96997b80678 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -25,8 +25,12 @@ public static bool PortableDataLocationInUse() return false; } + public static readonly string VersionLogDirectory = Path.Combine(LogDirectory, Constant.Version); + + public static readonly string LogDirectory = Path.Combine(DataDirectory(), Constant.Logs); public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); + public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectorty, Constant.Plugins); public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index 2892a065445..ee684e7ca21 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -115,7 +115,7 @@ private void ClearLogFolder() private static DirectoryInfo GetLogDir(string version = "") { - return new DirectoryInfo(Path.Combine(DataLocation.DataDirectory(), Constant.Logs, version)); + return new DirectoryInfo(Path.Combine(DataLocation.LogDirectory, version)); } private static List GetLogFiles(string version = "") diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 8d8ccb78041..fc9122b536e 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -420,7 +420,7 @@ public FamilyTypeface SelectedResultSubFontFaces [RelayCommand] private void OpenThemesFolder() { - App.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Themes)); + App.API.OpenDirectory(DataLocation.ThemesDirectory); } public void UpdateColorScheme() diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 2331ee68c05..13bba04b47b 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -147,8 +147,6 @@ private static unsafe bool EnableShutdownPrivilege() private List Commands() { var results = new List(); - var logPath = Path.Combine(DataLocation.DataDirectory(), "Logs", Constant.Version); - var userDataPath = DataLocation.DataDirectory(); var recycleBinFolder = "shell:RecycleBinFolder"; results.AddRange(new[] { @@ -406,11 +404,11 @@ private List Commands() Title = "Open Log Location", SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_open_log_location"), IcoPath = "Images\\app.png", - CopyText = logPath, - AutoCompleteText = logPath, + CopyText = DataLocation.VersionLogDirectory, + AutoCompleteText = DataLocation.VersionLogDirectory, Action = c => { - context.API.OpenDirectory(logPath); + context.API.OpenDirectory(DataLocation.VersionLogDirectory); return true; } }, @@ -432,11 +430,11 @@ private List Commands() Title = "Flow Launcher UserData Folder", SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_open_userdata_location"), IcoPath = "Images\\app.png", - CopyText = userDataPath, - AutoCompleteText = userDataPath, + CopyText = DataLocation.DataDirectory(), + AutoCompleteText = DataLocation.DataDirectory(), Action = c => { - context.API.OpenDirectory(userDataPath); + context.API.OpenDirectory(DataLocation.DataDirectory()); return true; } }, From 3efe550b7f2d5008a20e749258e452062edb7ad6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:28:58 +0800 Subject: [PATCH 10/27] Use context plugin settings path --- .../Environments/AbstractPluginEnvironment.cs | 3 ++- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 3 +-- Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs | 12 +----------- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 1 + .../Storage/PluginJsonStorage.cs | 8 -------- 5 files changed, 5 insertions(+), 22 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 93058759a10..5f507021c66 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -115,10 +115,11 @@ private IEnumerable SetPathForPluginPairs(string filePath, string la { if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) { - pluginPairs.Add(CreatePluginPair(filePath, metadata)); metadata.AssemblyName = string.Empty; metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + + pluginPairs.Add(CreatePluginPair(filePath, metadata)); } } diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index f6b200aa520..88d595301be 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Microsoft.IO; using System.Windows; @@ -31,7 +30,7 @@ internal abstract class JsonRPCPlugin : JsonRPCPluginBase private int RequestId { get; set; } private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml"); - private string SettingPath => Path.Combine(DataLocation.PluginSettingsDirectory, Context.CurrentPluginMetadata.Name, "Settings.json"); + private string SettingPath => Path.Combine(Context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "Settings.json"); public override List LoadContextMenus(Result selectedResult) { diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs index 947221e778c..c0852958ecc 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs @@ -6,7 +6,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -28,8 +27,7 @@ internal abstract class JsonRPCPluginBase : IAsyncPlugin, IContextMenu, ISetting private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml"); - private string SettingDirectory => Path.Combine(DataLocation.PluginSettingsDirectory, - Context.CurrentPluginMetadata.Name); + private string SettingDirectory => Context.CurrentPluginMetadata.PluginSettingsDirectoryPath; private string SettingPath => Path.Combine(SettingDirectory, "Settings.json"); @@ -145,13 +143,5 @@ public Control CreateSettingPanel() { return Settings.CreateSettingPanel(); } - - public void DeletePluginSettingsDirectory() - { - if (Directory.Exists(SettingDirectory)) - { - Directory.Delete(SettingDirectory, true); - } - } } } diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 91177e74545..6e9bcd05fee 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -75,6 +75,7 @@ public static IEnumerable DotNetPlugins(List source) plugin = Activator.CreateInstance(type) as IAsyncPlugin; + // Same as PluginJsonStorage.cs constructor var assemblyName = assembly.GetName().Name; metadata.AssemblyName = assemblyName; metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, assemblyName); diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index 42453a7a136..b377c81aa14 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -23,13 +23,5 @@ public PluginJsonStorage(T data) : this() { Data = data; } - - public void DeleteDirectory() - { - if (Directory.Exists(DirectoryPath)) - { - Directory.Delete(DirectoryPath, true); - } - } } } From 126153bf20b258babca833fb35e0f22a7d10f15f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:35:17 +0800 Subject: [PATCH 11/27] Improve plugin settings directory clean & Support plugin cache directory clean --- Flow.Launcher.Core/Plugin/PluginManager.cs | 59 +++++++++------------- Flow.Launcher/Languages/en.xaml | 2 + Flow.Launcher/PublicAPIInstance.cs | 5 +- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 7f73bf1cf18..9462df7408f 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -546,50 +546,41 @@ internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFro if (removePluginSettings) { - if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader + // For dotnet plugins, we need to remove their PluginJsonStorage instance + if (AllowedLanguage.IsDotNet(plugin.Language)) { - // if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin - // so we need to remove it from the api instance var method = API.GetType().GetMethod("RemovePluginSettings"); - var pluginJsonStorage = method?.Invoke(API, new object[] { plugin.AssemblyName }); + method?.Invoke(API, new object[] { plugin.AssemblyName }); + } - // if there exists a json storage for current plugin, we need to delete the directory path - if (pluginJsonStorage != null) - { - var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory"); - try - { - deleteMethod?.Invoke(pluginJsonStorage, null); - } - catch (Exception e) - { - Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); - API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), - string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); - } - } + try + { + var pluginSettingsDirectory = plugin.PluginSettingsDirectoryPath; + if (Directory.Exists(pluginSettingsDirectory)) + Directory.Delete(pluginSettingsDirectory, true); } - else // the plugin with json prc interface + catch (Exception e) { - var pluginPair = AllPlugins.FirstOrDefault(p => p.Metadata.ID == plugin.ID); - if (pluginPair != null && pluginPair.Plugin is JsonRPCPlugin jsonRpcPlugin) - { - try - { - jsonRpcPlugin.DeletePluginSettingsDirectory(); - } - catch (Exception e) - { - Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e); - API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), - string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); - } - } + Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin settings folder for {plugin.Name}", e); + API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"), + string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name)); } } if (removePluginFromSettings) { + try + { + var pluginCacheDirectory = plugin.PluginCacheDirectoryPath; + if (Directory.Exists(pluginCacheDirectory)) + Directory.Delete(pluginCacheDirectory, true); + } + catch (Exception e) + { + Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin cache folder for {plugin.Name}", e); + API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"), + string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name)); + } Settings.Plugins.Remove(plugin.ID); AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); } diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index c66772c83c5..cbef73b84f8 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -131,6 +131,8 @@ Uninstall Fail to remove plugin settings Plugins: {0} - Fail to remove plugin settings files, please remove them manually + Fail to remove plugin cache + Plugins: {0} - Fail to remove plugin cache files, please remove them manually Plugin Store diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index e5bc74958d7..cfbe0e9519a 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -189,7 +189,7 @@ public void LogException(string className, string message, Exception e, private readonly ConcurrentDictionary _pluginJsonStorages = new(); - public object RemovePluginSettings(string assemblyName) + public void RemovePluginSettings(string assemblyName) { foreach (var keyValuePair in _pluginJsonStorages) { @@ -199,11 +199,8 @@ public object RemovePluginSettings(string assemblyName) if (name == assemblyName) { _pluginJsonStorages.Remove(key, out var pluginJsonStorage); - return pluginJsonStorage; } } - - return null; } /// From 3106b025e399ce37ecdd2a63cf5b6d9c90c95b93 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 13:46:58 +0800 Subject: [PATCH 12/27] Support plugin directory update & validate --- .../Environments/AbstractPluginEnvironment.cs | 3 --- Flow.Launcher.Core/Plugin/PluginManager.cs | 27 ++++++++++++++++--- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 10 +------ Flow.Launcher.Infrastructure/Logger/Log.cs | 2 +- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 5f507021c66..7ed5f903f6b 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -116,9 +116,6 @@ private IEnumerable SetPathForPluginPairs(string filePath, string la if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) { metadata.AssemblyName = string.Empty; - metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); - metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); - pluginPairs.Add(CreatePluginPair(filePath, metadata)); } } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 9462df7408f..c88937c7543 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -32,7 +32,7 @@ public static class PluginManager private static PluginsSettings Settings; private static List _metadatas; - private static List _modifiedPlugins = new List(); + private static List _modifiedPlugins = new(); /// /// Directories that will hold Flow Launcher plugin directory @@ -152,6 +152,27 @@ public static void LoadPlugins(PluginsSettings settings) Settings = settings; Settings.UpdatePluginSettings(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); + UpdateAndValidatePluginDirectory(_metadatas); + } + + private static void UpdateAndValidatePluginDirectory(List metadatas) + { + foreach (var metadata in metadatas) + { + if (AllowedLanguage.IsDotNet(metadata.Language)) + { + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName); + } + else + { + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + } + + Helper.ValidateDirectory(metadata.PluginSettingsDirectoryPath); + Helper.ValidateDirectory(metadata.PluginCacheDirectoryPath); + } } /// @@ -226,11 +247,9 @@ public static ICollection ValidPluginsForQuery(Query query) if (query is null) return Array.Empty(); - if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword)) + if (!NonGlobalPlugins.TryGetValue(query.ActionKeyword, out var plugin)) return GlobalPlugins; - - var plugin = NonGlobalPlugins[query.ActionKeyword]; return new List { plugin diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 6e9bcd05fee..03f2ed4c6df 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -75,11 +75,7 @@ public static IEnumerable DotNetPlugins(List source) plugin = Activator.CreateInstance(type) as IAsyncPlugin; - // Same as PluginJsonStorage.cs constructor - var assemblyName = assembly.GetName().Name; - metadata.AssemblyName = assemblyName; - metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, assemblyName); - metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, assemblyName); + metadata.AssemblyName = assembly.GetName().Name; } #if DEBUG catch (Exception) @@ -149,8 +145,6 @@ public static IEnumerable ExecutablePlugins(IEnumerable ExecutableV2Plugins(IEnumerable Date: Mon, 24 Feb 2025 14:11:45 +0800 Subject: [PATCH 13/27] Fix log directory fetch issue --- Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 96997b80678..ed217976052 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -25,9 +25,9 @@ public static bool PortableDataLocationInUse() return false; } - public static readonly string VersionLogDirectory = Path.Combine(LogDirectory, Constant.Version); + public static string VersionLogDirectory => Path.Combine(LogDirectory, Constant.Version); + public static string LogDirectory => Path.Combine(DataDirectory(), Constant.Logs); - public static readonly string LogDirectory = Path.Combine(DataDirectory(), Constant.Logs); public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); From 58de62565ab9c86e00ef6433ab937d623a45db60 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 14:14:19 +0800 Subject: [PATCH 14/27] Do not validate plugin settings & cache path --- Flow.Launcher.Core/Plugin/PluginManager.cs | 7 ++----- Flow.Launcher.Plugin/PluginMetadata.cs | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index c88937c7543..bbd189efb7d 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -152,10 +152,10 @@ public static void LoadPlugins(PluginsSettings settings) Settings = settings; Settings.UpdatePluginSettings(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); - UpdateAndValidatePluginDirectory(_metadatas); + UpdatePluginDirectory(_metadatas); } - private static void UpdateAndValidatePluginDirectory(List metadatas) + private static void UpdatePluginDirectory(List metadatas) { foreach (var metadata in metadatas) { @@ -169,9 +169,6 @@ private static void UpdateAndValidatePluginDirectory(List metada metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); } - - Helper.ValidateDirectory(metadata.PluginSettingsDirectoryPath); - Helper.ValidateDirectory(metadata.PluginCacheDirectoryPath); } } diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 6dc8acc6338..dae8f58fd25 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -123,7 +123,7 @@ internal set public int QueryCount { get; set; } /// - /// The path to the plugin settings directory. + /// The path to the plugin settings directory which is not validated. /// It is used to store plugin settings files and data files. /// When plugin is deleted, FL will ask users whether to keep its settings. /// If users do not want to keep, this directory will be deleted. @@ -132,7 +132,7 @@ internal set public string PluginSettingsDirectoryPath { get; internal set; } /// - /// The path to the plugin cache directory. + /// The path to the plugin cache directory which is not validated. /// It is used to store cache files. /// When plugin is deleted, this directory will be deleted as well. /// From 47adfd12868c155fb0de621f9b16f72b6b336c93 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:15:02 +0800 Subject: [PATCH 15/27] Improve code quality --- .../Storage/BinaryStorage.cs | 23 +++++++------------ .../UserSettings/DataLocation.cs | 1 + 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 48c5fc1419b..5b73faae6a6 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -1,9 +1,4 @@ -using System; -using System.IO; -using System.Reflection; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters; -using System.Runtime.Serialization.Formatters.Binary; +using System.IO; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; @@ -16,18 +11,16 @@ namespace Flow.Launcher.Infrastructure.Storage /// Normally, it has better performance, but not readable /// /// - /// It utilize MemoryPack, which means the object must be MemoryPackSerializable - /// https://github.com/Cysharp/MemoryPack + /// It utilize MemoryPack, which means the object must be MemoryPackSerializable /// public class BinaryStorage { - const string DirectoryName = Constant.Cache; + public const string FileSuffix = ".cache"; - const string FileSuffix = ".cache"; - - public BinaryStorage(string filename) + // Let the derived class to set the file path + public BinaryStorage(string filename, string directoryPath = null) { - var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName); + directoryPath ??= DataLocation.CacheDirectory; Helper.ValidateDirectory(directoryPath); FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}"); @@ -58,14 +51,14 @@ public async ValueTask TryLoadAsync(T defaultData) } } - private async ValueTask DeserializeAsync(Stream stream, T defaultData) + private static async ValueTask DeserializeAsync(Stream stream, T defaultData) { try { var t = await MemoryPackSerializer.DeserializeAsync(stream); return t; } - catch (System.Exception e) + catch (System.Exception) { // Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e); return defaultData; diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index ed217976052..53812ef155f 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -28,6 +28,7 @@ public static bool PortableDataLocationInUse() public static string VersionLogDirectory => Path.Combine(LogDirectory, Constant.Version); public static string LogDirectory => Path.Combine(DataDirectory(), Constant.Logs); + public static readonly string CacheDirectory = Path.Combine(DataDirectory(), Constant.Cache); public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); From a0c2a42e17d79f464d0a64f0b70e75b42cb8a214 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:15:26 +0800 Subject: [PATCH 16/27] Let Program plugin use plugin cache path --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 39 +++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index b3763aaa69c..6e1cdffb5c7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; +using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.Program.Programs; using Flow.Launcher.Plugin.Program.Views; using Flow.Launcher.Plugin.Program.Views.Models; @@ -188,9 +191,41 @@ public async Task InitAsync(PluginInitContext context) await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () => { - _win32Storage = new BinaryStorage("Win32"); + Helper.ValidateDirectory(Context.CurrentPluginMetadata.PluginCacheDirectoryPath); + + static bool MoveFile(string sourcePath, string destinationPath) + { + if (!File.Exists(sourcePath)) + { + return false; + } + + if (File.Exists(destinationPath)) + { + File.Delete(sourcePath); + return false; + } + + var destinationDirectory = Path.GetDirectoryName(destinationPath); + if (!Directory.Exists(destinationDirectory) && (!string.IsNullOrEmpty(destinationDirectory))) + { + Directory.CreateDirectory(destinationDirectory); + } + File.Move(sourcePath, destinationPath); + return true; + } + + // Move old cache files to the new cache directory + var oldWin32CacheFile = Path.Combine(DataLocation.CacheDirectory, $"Win32.cache"); + var newWin32CacheFile = Path.Combine(Context.CurrentPluginMetadata.PluginCacheDirectoryPath, $"Win32.cache"); + MoveFile(oldWin32CacheFile, newWin32CacheFile); + var oldUWPCacheFile = Path.Combine(DataLocation.CacheDirectory, $"UWP.cache"); + var newUWPCacheFile = Path.Combine(Context.CurrentPluginMetadata.PluginCacheDirectoryPath, $"UWP.cache"); + MoveFile(oldUWPCacheFile, newUWPCacheFile); + + _win32Storage = new BinaryStorage("Win32", Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _win32s = await _win32Storage.TryLoadAsync(Array.Empty()); - _uwpStorage = new BinaryStorage("UWP"); + _uwpStorage = new BinaryStorage("UWP", Context.CurrentPluginMetadata.PluginCacheDirectoryPath); _uwps = await _uwpStorage.TryLoadAsync(Array.Empty()); }); Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); From a29ed64f3c526b09a060ec18a0f53963afed3b7b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:27:04 +0800 Subject: [PATCH 17/27] Use metadata for plugin settings directory --- Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index ce53c7da5c4..7ad9715bbd8 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Infrastructure; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.WebSearch @@ -183,9 +182,8 @@ void Init() DefaultImagesDirectory = Path.Combine(pluginDirectory, Images); Helper.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory); - // Custom images directory is in the WebSearch's data location folder - var name = Path.GetFileNameWithoutExtension(_context.CurrentPluginMetadata.ExecuteFileName); - CustomImagesDirectory = Path.Combine(DataLocation.PluginSettingsDirectory, name, "CustomIcons"); + // Custom images directory is in the WebSearch's data location folder + CustomImagesDirectory = Path.Combine(_context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "CustomIcons"); }; } From 65ae342bcaabf79ab3d010e2b260e40de57f5f8d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 15:37:13 +0800 Subject: [PATCH 18/27] Add documents for Flow.Launcher.Plugin --- Flow.Launcher.Plugin/ActionContext.cs | 3 ++ .../Interfaces/IResultUpdated.cs | 27 +++++++++++- .../Interfaces/ISettingProvider.cs | 7 ++++ Flow.Launcher.Plugin/PluginInitContext.cs | 8 ++++ Flow.Launcher.Plugin/PluginPair.cs | 28 +++++++++++-- Flow.Launcher.Plugin/Query.cs | 7 ++-- Flow.Launcher.Plugin/Result.cs | 2 - .../SharedCommands/SearchWeb.cs | 5 ++- .../SharedCommands/ShellCommand.cs | 23 ++++++++++ .../SharedModels/MatchResult.cs | 42 +++++++++++++++++++ 10 files changed, 141 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Plugin/ActionContext.cs b/Flow.Launcher.Plugin/ActionContext.cs index e31c8e31d8b..9e05bbd0617 100644 --- a/Flow.Launcher.Plugin/ActionContext.cs +++ b/Flow.Launcher.Plugin/ActionContext.cs @@ -51,6 +51,9 @@ public ModifierKeys ToModifierKeys() (WinPressed ? ModifierKeys.Windows : ModifierKeys.None); } + /// + /// Default object with all keys not pressed. + /// public static readonly SpecialKeyState Default = new () { CtrlPressed = false, ShiftPressed = false, diff --git a/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs b/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs index fd21460ac55..aa4e4a56db5 100644 --- a/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs +++ b/Flow.Launcher.Plugin/Interfaces/IResultUpdated.cs @@ -4,17 +4,42 @@ namespace Flow.Launcher.Plugin { + /// + /// Interface for plugins that want to manually update their results + /// public interface IResultUpdated : IFeatures { + /// + /// Event that is triggered when the results are updated + /// event ResultUpdatedEventHandler ResultsUpdated; } + /// + /// Delegate for the ResultsUpdated event + /// + /// + /// public delegate void ResultUpdatedEventHandler(IResultUpdated sender, ResultUpdatedEventArgs e); + /// + /// Event arguments for the ResultsUpdated event + /// public class ResultUpdatedEventArgs : EventArgs { + /// + /// List of results that should be displayed + /// public List Results; + + /// + /// Query that triggered the update + /// public Query Query; + + /// + /// Token that can be used to cancel the update + /// public CancellationToken Token { get; init; } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs b/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs index d5ffba20b9b..f034243c3b4 100644 --- a/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISettingProvider.cs @@ -2,8 +2,15 @@ namespace Flow.Launcher.Plugin { + /// + /// This interface is used to create settings panel for .Net plugins + /// public interface ISettingProvider { + /// + /// Create settings panel control for .Net plugins + /// + /// Control CreateSettingPanel(); } } diff --git a/Flow.Launcher.Plugin/PluginInitContext.cs b/Flow.Launcher.Plugin/PluginInitContext.cs index f040752bd60..a42e3930cb1 100644 --- a/Flow.Launcher.Plugin/PluginInitContext.cs +++ b/Flow.Launcher.Plugin/PluginInitContext.cs @@ -5,10 +5,18 @@ /// public class PluginInitContext { + /// + /// Default constructor. + /// public PluginInitContext() { } + /// + /// Constructor. + /// + /// + /// public PluginInitContext(PluginMetadata currentPluginMetadata, IPublicAPI api) { CurrentPluginMetadata = currentPluginMetadata; diff --git a/Flow.Launcher.Plugin/PluginPair.cs b/Flow.Launcher.Plugin/PluginPair.cs index 7bf6346910c..037af7427a6 100644 --- a/Flow.Launcher.Plugin/PluginPair.cs +++ b/Flow.Launcher.Plugin/PluginPair.cs @@ -1,21 +1,37 @@ namespace Flow.Launcher.Plugin { + /// + /// Plugin instance and plugin metadata + /// public class PluginPair { + /// + /// Plugin instance + /// public IAsyncPlugin Plugin { get; internal set; } - public PluginMetadata Metadata { get; internal set; } - + /// + /// Plugin metadata + /// + public PluginMetadata Metadata { get; internal set; } + /// + /// Convert to string + /// + /// public override string ToString() { return Metadata.Name; } + /// + /// Compare by plugin metadata ID + /// + /// + /// public override bool Equals(object obj) { - PluginPair r = obj as PluginPair; - if (r != null) + if (obj is PluginPair r) { return string.Equals(r.Metadata.ID, Metadata.ID); } @@ -25,6 +41,10 @@ public override bool Equals(object obj) } } + /// + /// Get hash coode + /// + /// public override int GetHashCode() { var hashcode = Metadata.ID?.GetHashCode() ?? 0; diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index e182491c2f0..a9694e263a7 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -2,10 +2,11 @@ namespace Flow.Launcher.Plugin { + /// + /// Represents a query that is sent to a plugin. + /// public class Query { - public Query() { } - /// /// Raw query, this includes action keyword if it has /// We didn't recommend use this property directly. You should always use Search property. @@ -55,13 +56,13 @@ public Query() { } /// public string ActionKeyword { get; init; } - [JsonIgnore] /// /// Splits by spaces and returns the first item. /// /// /// returns an empty string when does not have enough items. /// + [JsonIgnore] public string FirstSearch => SplitSearch(0); [JsonIgnore] diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 9b16cc1cbf0..9104854389d 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -13,7 +12,6 @@ namespace Flow.Launcher.Plugin /// public class Result { - private string _pluginDirectory; private string _icoPath; diff --git a/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs b/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs index a7744ffaca6..752c85933d6 100644 --- a/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs +++ b/Flow.Launcher.Plugin/SharedCommands/SearchWeb.cs @@ -6,6 +6,9 @@ namespace Flow.Launcher.Plugin.SharedCommands { + /// + /// Contains methods to open a search in a new browser window or tab. + /// public static class SearchWeb { private static string GetDefaultBrowserPath() @@ -106,4 +109,4 @@ public static void OpenInBrowserTab(this string url, string browserPath = "", bo } } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs index a0440e30de9..288222d4ff1 100644 --- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs +++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs @@ -8,12 +8,26 @@ namespace Flow.Launcher.Plugin.SharedCommands { + /// + /// Contains methods for running shell commands + /// public static class ShellCommand { + /// + /// Delegate for EnumThreadWindows + /// + /// + /// + /// public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam); private static bool containsSecurityWindow; + /// + /// Runs a windows command using the provided ProcessStartInfo + /// + /// + /// public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo) { processStartInfo.Verb = "RunAsUser"; @@ -65,6 +79,15 @@ private static unsafe string GetWindowTitle(HWND hwnd) return buffer[..length].ToString(); } + /// + /// Runs a windows command using the provided ProcessStartInfo + /// + /// + /// + /// + /// + /// + /// public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "", bool createNoWindow = false) { diff --git a/Flow.Launcher.Plugin/SharedModels/MatchResult.cs b/Flow.Launcher.Plugin/SharedModels/MatchResult.cs index 5144eb61d66..36677d4bb29 100644 --- a/Flow.Launcher.Plugin/SharedModels/MatchResult.cs +++ b/Flow.Launcher.Plugin/SharedModels/MatchResult.cs @@ -2,14 +2,29 @@ namespace Flow.Launcher.Plugin.SharedModels { + /// + /// Represents the result of a match operation. + /// public class MatchResult { + /// + /// Initializes a new instance of the class. + /// + /// + /// public MatchResult(bool success, SearchPrecisionScore searchPrecision) { Success = success; SearchPrecision = searchPrecision; } + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// public MatchResult(bool success, SearchPrecisionScore searchPrecision, List matchData, int rawScore) { Success = success; @@ -18,6 +33,9 @@ public MatchResult(bool success, SearchPrecisionScore searchPrecision, List RawScore = rawScore; } + /// + /// Whether the match operation was successful. + /// public bool Success { get; set; } /// @@ -30,6 +48,9 @@ public MatchResult(bool success, SearchPrecisionScore searchPrecision, List /// private int _rawScore; + /// + /// The raw calculated search score without any search precision filtering applied. + /// public int RawScore { get { return _rawScore; } @@ -45,8 +66,15 @@ public int RawScore /// public List MatchData { get; set; } + /// + /// The search precision score used to filter the search results. + /// public SearchPrecisionScore SearchPrecision { get; set; } + /// + /// Determines if the search precision score is met. + /// + /// public bool IsSearchPrecisionScoreMet() { return IsSearchPrecisionScoreMet(_rawScore); @@ -63,10 +91,24 @@ private int ScoreAfterSearchPrecisionFilter(int rawScore) } } + /// + /// Represents the search precision score used to filter search results. + /// public enum SearchPrecisionScore { + /// + /// The highest search precision score. + /// Regular = 50, + + /// + /// The medium search precision score. + /// Low = 20, + + /// + /// The lowest search precision score. + /// None = 0 } } From 66228151aeb437339aca65769d3facaeb8819776 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 16:08:06 +0800 Subject: [PATCH 19/27] Fix typos --- Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs | 4 ++-- .../SettingPages/ViewModels/SettingsPaneAboutViewModel.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs index 53812ef155f..5b948e4508f 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/DataLocation.cs @@ -29,11 +29,11 @@ public static bool PortableDataLocationInUse() public static string LogDirectory => Path.Combine(DataDirectory(), Constant.Logs); public static readonly string CacheDirectory = Path.Combine(DataDirectory(), Constant.Cache); - public static readonly string SettingsDirectorty = Path.Combine(DataDirectory(), Constant.Settings); + public static readonly string SettingsDirectory = Path.Combine(DataDirectory(), Constant.Settings); public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins); public static readonly string ThemesDirectory = Path.Combine(DataDirectory(), Constant.Themes); - public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectorty, Constant.Plugins); + public static readonly string PluginSettingsDirectory = Path.Combine(SettingsDirectory, Constant.Plugins); public static readonly string PluginCacheDirectory = Path.Combine(DataDirectory(), Constant.Cache, Constant.Plugins); public const string PythonEnvironmentName = "Python"; diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs index ee684e7ca21..adbafe31e0d 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs @@ -77,13 +77,13 @@ private void AskClearLogFolderConfirmation() [RelayCommand] private void OpenSettingsFolder() { - PluginManager.API.OpenDirectory(DataLocation.SettingsDirectorty); + PluginManager.API.OpenDirectory(DataLocation.SettingsDirectory); } [RelayCommand] private void OpenParentOfSettingsFolder(object parameter) { - string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectorty); + string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectory); string parentFolderPath = Path.GetDirectoryName(settingsFolderPath); PluginManager.API.OpenDirectory(parentFolderPath); } From fe86e23dead49655301f1608d1e7789b26b66e61 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 24 Feb 2025 16:11:49 +0800 Subject: [PATCH 20/27] Add exception handles --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 34 ++++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 6e1cdffb5c7..3be23214c86 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -193,26 +193,46 @@ await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs { Helper.ValidateDirectory(Context.CurrentPluginMetadata.PluginCacheDirectoryPath); - static bool MoveFile(string sourcePath, string destinationPath) + static void MoveFile(string sourcePath, string destinationPath) { if (!File.Exists(sourcePath)) { - return false; + return; } if (File.Exists(destinationPath)) { - File.Delete(sourcePath); - return false; + try + { + File.Delete(sourcePath); + } + catch (Exception) + { + // Ignore, we will handle next time we start the plugin + } + return; } var destinationDirectory = Path.GetDirectoryName(destinationPath); if (!Directory.Exists(destinationDirectory) && (!string.IsNullOrEmpty(destinationDirectory))) { - Directory.CreateDirectory(destinationDirectory); + try + { + Directory.CreateDirectory(destinationDirectory); + } + catch (Exception) + { + // Ignore, we will handle next time we start the plugin + } + } + try + { + File.Move(sourcePath, destinationPath); + } + catch (Exception) + { + // Ignore, we will handle next time we start the plugin } - File.Move(sourcePath, destinationPath); - return true; } // Move old cache files to the new cache directory From f8d0981898c3fec208da6a8a1bc6e76183035e62 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 25 Feb 2025 10:22:39 +0800 Subject: [PATCH 21/27] Update json rpc plugin directory before loading plugins --- Flow.Launcher.Core/Plugin/PluginManager.cs | 24 +++++++++++++++------- Flow.Launcher.Plugin/PluginMetadata.cs | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index bbd189efb7d..c8fe7e8182b 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -151,11 +151,26 @@ public static void LoadPlugins(PluginsSettings settings) _metadatas = PluginConfig.Parse(Directories); Settings = settings; Settings.UpdatePluginSettings(_metadatas); + // Update Json RPC plugin directory before loading plugins so that we can pass the correct plugin directory + UpdateJsonRPCPluginDirectory(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); - UpdatePluginDirectory(_metadatas); + // Update dotnet plugin directory after loading plugins because we need to get assembly name first + UpdateNotNetPluginDirectory(_metadatas); } - private static void UpdatePluginDirectory(List metadatas) + private static void UpdateJsonRPCPluginDirectory(List metadatas) + { + foreach (var metadata in metadatas) + { + if (!AllowedLanguage.IsDotNet(metadata.Language)) + { + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + } + } + } + + private static void UpdateNotNetPluginDirectory(List metadatas) { foreach (var metadata in metadatas) { @@ -164,11 +179,6 @@ private static void UpdatePluginDirectory(List metadatas) metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName); metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName); } - else - { - metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); - metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); - } } } diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index dae8f58fd25..259716ec13c 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -9,8 +9,6 @@ namespace Flow.Launcher.Plugin /// public class PluginMetadata : BaseModel { - private string _pluginDirectory; - /// /// Plugin ID. /// @@ -69,6 +67,8 @@ public class PluginMetadata : BaseModel [JsonIgnore] public string AssemblyName { get; internal set; } + private string _pluginDirectory; + /// /// Plugin source directory. /// From ce3a3e912aac6bfb74e684aa4320cd193272f2bd Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Mar 2025 19:43:16 +0800 Subject: [PATCH 22/27] Fix plugin settings delete issue --- Flow.Launcher.Core/Plugin/PluginManager.cs | 37 ++++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index ce3f1ac6d73..4d8bf76b7e6 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -72,15 +72,20 @@ public static async ValueTask DisposePluginsAsync() { foreach (var pluginPair in AllPlugins) { - switch (pluginPair.Plugin) - { - case IDisposable disposable: - disposable.Dispose(); - break; - case IAsyncDisposable asyncDisposable: - await asyncDisposable.DisposeAsync(); - break; - } + await DisposePluginAsync(pluginPair); + } + } + + private static async Task DisposePluginAsync(PluginPair pluginPair) + { + switch (pluginPair.Plugin) + { + case IDisposable disposable: + disposable.Dispose(); + break; + case IAsyncDisposable asyncDisposable: + await asyncDisposable.DisposeAsync(); + break; } } @@ -565,13 +570,25 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c } } - internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) + internal static async void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { throw new ArgumentException($"Plugin {plugin.Name} has been modified"); } + if (removePluginFromSettings) + { + // If we want to remove plugin from AllPlugins, + // we need to dispose them so that they can release file handles + // which can help FL to delete the plugin settings & cache folders successfully + var pluginPairs = AllPlugins.FindAll(p => p.Metadata.ID == plugin.ID); + foreach (var pluginPair in pluginPairs) + { + await DisposePluginAsync(pluginPair); + } + } + if (removePluginSettings) { // For dotnet plugins, we need to remove their PluginJsonStorage instance From 486cc6ac4985bbdd7360332b718a77e4f015278a Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Mar 2025 20:15:49 +0800 Subject: [PATCH 23/27] Fix async task issue --- Flow.Launcher.Core/Plugin/PluginManager.cs | 10 +++++----- .../PluginsManager.cs | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 4d8bf76b7e6..456f0a699e6 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -469,10 +469,10 @@ public static bool PluginModified(string uuid) /// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url, /// unless it's a local path installation /// - public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) + public static async Task UpdatePluginAsync(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath) { InstallPlugin(newVersion, zipFilePath, checkModified:false); - UninstallPlugin(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); + await UninstallPluginAsync(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false); _modifiedPlugins.Add(existingVersion.ID); } @@ -487,9 +487,9 @@ public static void InstallPlugin(UserPlugin plugin, string zipFilePath) /// /// Uninstall a plugin. /// - public static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) + public static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false) { - UninstallPlugin(plugin, removePluginFromSettings, removePluginSettings, true); + await UninstallPluginAsync(plugin, removePluginFromSettings, removePluginSettings, true); } #endregion @@ -570,7 +570,7 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c } } - internal static async void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) + internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified) { if (checkModified && PluginModified(plugin.ID)) { diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index f4c8a66da31..07bbfdaa0c1 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -341,7 +341,7 @@ await DownloadFileAsync( } else { - PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin, + await PluginManager.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlugin, downloadToFilePath); if (Settings.AutoRestartAfterChanging) @@ -433,7 +433,7 @@ await DownloadFileAsync( if (cts.IsCancellationRequested) return; else - PluginManager.UpdatePlugin(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, + await PluginManager.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin, downloadToFilePath); } catch (Exception ex) @@ -681,7 +681,7 @@ internal List RequestUninstall(string search) Title = $"{x.Metadata.Name} by {x.Metadata.Author}", SubTitle = x.Metadata.Description, IcoPath = x.Metadata.IcoPath, - Action = e => + AsyncAction = async e => { string message; if (Settings.AutoRestartAfterChanging) @@ -704,7 +704,7 @@ internal List RequestUninstall(string search) MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Context.API.HideMainWindow(); - Uninstall(x.Metadata); + await UninstallAsync(x.Metadata); if (Settings.AutoRestartAfterChanging) { Context.API.RestartApp(); @@ -729,7 +729,7 @@ internal List RequestUninstall(string search) return Search(results, search); } - private void Uninstall(PluginMetadata plugin) + private async Task UninstallAsync(PluginMetadata plugin) { try { @@ -737,7 +737,7 @@ private void Uninstall(PluginMetadata plugin) Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_subtitle"), Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_title"), button: MessageBoxButton.YesNo) == MessageBoxResult.No; - PluginManager.UninstallPlugin(plugin, removePluginFromSettings: true, removePluginSettings: removePluginSettings); + await PluginManager.UninstallPluginAsync(plugin, removePluginFromSettings: true, removePluginSettings: removePluginSettings); } catch (ArgumentException e) { From af3b3916764f355568ac6117b123287ef255d54f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 6 Mar 2025 20:20:30 +0800 Subject: [PATCH 24/27] Fix dispose --- Flow.Launcher.Core/Plugin/PluginManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 456f0a699e6..76d83cbcf4a 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -577,7 +577,7 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool remo throw new ArgumentException($"Plugin {plugin.Name} has been modified"); } - if (removePluginFromSettings) + if (removePluginSettings || removePluginFromSettings) { // If we want to remove plugin from AllPlugins, // we need to dispose them so that they can release file handles From b0b1a2661ad2efb23080d6ed438f0aca5bae2030 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 16 Mar 2025 20:26:06 +0800 Subject: [PATCH 25/27] Fix build issue & Cleanup codes --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 4a75ce3fb18..e629f887ee1 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Windows; @@ -435,7 +434,7 @@ private List Commands() AutoCompleteText = DataLocation.VersionLogDirectory, Action = c => { - _context.API.OpenDirectory(logPath); + _context.API.OpenDirectory(DataLocation.VersionLogDirectory); return true; } }, @@ -459,7 +458,7 @@ private List Commands() AutoCompleteText = DataLocation.DataDirectory(), Action = c => { - _context.API.OpenDirectory(userDataPath); + _context.API.OpenDirectory(DataLocation.DataDirectory()); return true; } }, From ee0b039427423c1d716a508ded71a830c101e606 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Mar 2025 12:44:49 +0800 Subject: [PATCH 26/27] Merge update plugin directory functions --- Flow.Launcher.Core/Plugin/PluginManager.cs | 25 +++++++--------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 3f07eef01a5..578243139c6 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -159,26 +159,12 @@ public static void LoadPlugins(PluginsSettings settings) _metadatas = PluginConfig.Parse(Directories); Settings = settings; Settings.UpdatePluginSettings(_metadatas); - // Update Json RPC plugin directory before loading plugins so that we can pass the correct plugin directory - UpdateJsonRPCPluginDirectory(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); - // Update dotnet plugin directory after loading plugins because we need to get assembly name first - UpdateNotNetPluginDirectory(_metadatas); + // Since dotnet plugins need to get assembly name first, we should update plugin directory after loading plugins + UpdatePluginDirectory(_metadatas); } - private static void UpdateJsonRPCPluginDirectory(List metadatas) - { - foreach (var metadata in metadatas) - { - if (!AllowedLanguage.IsDotNet(metadata.Language)) - { - metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); - metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); - } - } - } - - private static void UpdateNotNetPluginDirectory(List metadatas) + private static void UpdatePluginDirectory(List metadatas) { foreach (var metadata in metadatas) { @@ -187,6 +173,11 @@ private static void UpdateNotNetPluginDirectory(List metadatas) metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName); metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName); } + else + { + metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name); + metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name); + } } } From 5f976b933168de56e6e136af4b79da56a5e4688c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 23 Mar 2025 12:47:57 +0800 Subject: [PATCH 27/27] Remove useless assignment --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index f7f9affacc9..a64457ffc53 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -139,15 +138,11 @@ public static IEnumerable ExecutablePlugins(IEnumerable o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) .Select(metadata => { - var plugin = new PluginPair + return new PluginPair { Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata }; - - plugin.Metadata.AssemblyName = string.Empty; - - return plugin; }); } @@ -157,15 +152,11 @@ public static IEnumerable ExecutableV2Plugins(IEnumerable o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase)) .Select(metadata => { - var plugin = new PluginPair + return new PluginPair { Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata }; - - plugin.Metadata.AssemblyName = string.Empty; - - return plugin; }); } }