From a27d5336dffbb0a8cbcd2090dd857171f04f37f0 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Thu, 15 Feb 2024 22:06:19 -0600 Subject: [PATCH 01/19] fix python plugin and node plugin fix job process association the program plugin --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 2 -- Flow.Launcher.Core/Plugin/NodePluginV2.cs | 12 ++------ .../Plugin/ProcessStreamPluginV2.cs | 29 ++++++++++++------- Flow.Launcher.Core/Plugin/PythonPluginV2.cs | 12 ++++---- .../Programs/Win32.cs | 2 +- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 390da072b..1d2389b4f 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -15,8 +15,6 @@ namespace Flow.Launcher.Core.Plugin { internal abstract class JsonRPCPluginV2 : JsonRPCPluginBase, IAsyncDisposable, IAsyncReloadable, IResultUpdated { - public abstract string SupportedLanguage { get; set; } - public const string JsonRpc = "JsonRPC"; protected abstract IDuplexPipe ClientPipe { get; set; } diff --git a/Flow.Launcher.Core/Plugin/NodePluginV2.cs b/Flow.Launcher.Core/Plugin/NodePluginV2.cs index 6c95777f0..9fc9ccac3 100644 --- a/Flow.Launcher.Core/Plugin/NodePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/NodePluginV2.cs @@ -10,21 +10,13 @@ namespace Flow.Launcher.Core.Plugin /// /// Execution of JavaScript & TypeScript plugins /// - internal class NodePluginV2 : ProcessStreamPluginV2 + internal sealed class NodePluginV2 : ProcessStreamPluginV2 { public NodePluginV2(string filename) { - StartInfo = new ProcessStartInfo - { - FileName = filename, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - }; + StartInfo = new ProcessStartInfo { FileName = filename, }; } - public override string SupportedLanguage { get; set; } protected override ProcessStartInfo StartInfo { get; set; } public override async Task InitAsync(PluginInitContext context) diff --git a/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs b/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs index 16f9dfbf9..a14baf271 100644 --- a/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO.Pipelines; @@ -6,6 +8,7 @@ using System.Threading.Tasks; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin; using Meziantou.Framework.Win32; +using Microsoft.VisualBasic.ApplicationServices; using Nerdbank.Streams; namespace Flow.Launcher.Core.Plugin @@ -18,18 +21,18 @@ namespace Flow.Launcher.Core.Plugin { _jobObject.SetLimits(new JobObjectLimits() { - Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException + Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException | + JobObjectLimitFlags.SilentBreakawayOk }); - + _jobObject.AssignProcess(Process.GetCurrentProcess()); } - public override string SupportedLanguage { get; set; } - protected sealed override IDuplexPipe ClientPipe { get; set; } + protected sealed override IDuplexPipe ClientPipe { get; set; } = null!; protected abstract ProcessStartInfo StartInfo { get; set; } - protected Process ClientProcess { get; set; } + protected Process ClientProcess { get; set; } = null!; public override async Task InitAsync(PluginInitContext context) { @@ -37,12 +40,18 @@ namespace Flow.Launcher.Core.Plugin StartInfo.EnvironmentVariables["FLOW_PROGRAM_DIRECTORY"] = Constant.ProgramDirectory; StartInfo.EnvironmentVariables["FLOW_APPLICATION_DIRECTORY"] = Constant.ApplicationDirectory; - StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); + StartInfo.RedirectStandardError = true; + StartInfo.RedirectStandardInput = true; + StartInfo.RedirectStandardOutput = true; + StartInfo.CreateNoWindow = true; + StartInfo.UseShellExecute = false; StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; - ClientProcess = Process.Start(StartInfo); - ArgumentNullException.ThrowIfNull(ClientProcess); - + var process = Process.Start(StartInfo); + ArgumentNullException.ThrowIfNull(process); + ClientProcess = process; + _jobObject.AssignProcess(ClientProcess); + SetupPipe(ClientProcess); ErrorStream = ClientProcess.StandardError; diff --git a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs index 4a8d8d7de..f768daf15 100644 --- a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs @@ -18,7 +18,6 @@ namespace Flow.Launcher.Core.Plugin { internal sealed class PythonPluginV2 : ProcessStreamPluginV2 { - public override string SupportedLanguage { get; set; } = AllowedLanguage.Python; protected override ProcessStartInfo StartInfo { get; set; } public PythonPluginV2(string filename) @@ -26,11 +25,6 @@ namespace Flow.Launcher.Core.Plugin StartInfo = new ProcessStartInfo { FileName = filename, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true }; var path = Path.Combine(Constant.ProgramDirectory, JsonRpc); @@ -39,5 +33,11 @@ namespace Flow.Launcher.Core.Plugin //Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable StartInfo.ArgumentList.Add("-B"); } + + public override async Task InitAsync(PluginInitContext context) + { + StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); + await base.InitAsync(context); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 7d08d3670..a7f447598 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -196,7 +196,7 @@ namespace Flow.Launcher.Plugin.Program.Programs FileName = FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true, - Verb = runAsAdmin ? "runas" : "" + Verb = runAsAdmin ? "runas" : "", }; _ = Task.Run(() => Main.StartProcess(Process.Start, info)); From f6b17023dc059fda8fd83e30a2831cee89d2b875 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Thu, 15 Feb 2024 22:14:03 -0600 Subject: [PATCH 02/19] remove redundant information to merge into the process stream plugin --- Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs index ee1b315c2..128b28638 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs @@ -12,15 +12,7 @@ namespace Flow.Launcher.Core.Plugin public ExecutablePluginV2(string filename) { - StartInfo = new ProcessStartInfo - { - FileName = filename, - UseShellExecute = false, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true - }; + StartInfo = new ProcessStartInfo { FileName = filename }; } - } } From 692eb1301124fd800b50aea21590d39b3798da01 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Thu, 15 Feb 2024 22:15:22 -0600 Subject: [PATCH 03/19] remove some duplicate code --- Flow.Launcher.Core/Plugin/NodePluginV2.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/NodePluginV2.cs b/Flow.Launcher.Core/Plugin/NodePluginV2.cs index 9fc9ccac3..d84a72864 100644 --- a/Flow.Launcher.Core/Plugin/NodePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/NodePluginV2.cs @@ -22,8 +22,6 @@ namespace Flow.Launcher.Core.Plugin public override async Task InitAsync(PluginInitContext context) { StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); - StartInfo.ArgumentList.Add(string.Empty); - StartInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; await base.InitAsync(context); } } From 2882d188639b59c8cab7eb87c7a8d2fb4fb179c7 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sun, 18 Feb 2024 23:46:24 -0600 Subject: [PATCH 04/19] use HeaderDeliminatedHandler for NodePlugin while NewLineDeliminated for everything else --- .../Plugin/ExecutablePluginV2.cs | 2 ++ Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 18 ++++++++++++++++-- Flow.Launcher.Core/Plugin/NodePluginV2.cs | 2 ++ Flow.Launcher.Core/Plugin/PythonPluginV2.cs | 11 +++++------ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs index 128b28638..852f57b9f 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePluginV2.cs @@ -14,5 +14,7 @@ namespace Flow.Launcher.Core.Plugin { StartInfo = new ProcessStartInfo { FileName = filename }; } + + protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.NewLineDelimited; } } diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 1d2389b4f..1ad6edc2c 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -86,12 +86,26 @@ namespace Flow.Launcher.Core.Plugin public event ResultUpdatedEventHandler ResultsUpdated; + protected enum MessageHandlerType + { + HeaderDelimited, + LengthHeaderDelimited, + NewLineDelimited + } + + protected abstract MessageHandlerType MessageHandler { get; } + private void SetupJsonRPC() { var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption }; - var handler = new NewLineDelimitedMessageHandler(ClientPipe, - formatter); + IJsonRpcMessageHandler handler = MessageHandler switch + { + MessageHandlerType.HeaderDelimited => new HeaderDelimitedMessageHandler(ClientPipe, formatter), + MessageHandlerType.LengthHeaderDelimited => new LengthHeaderMessageHandler(ClientPipe, formatter), + MessageHandlerType.NewLineDelimited => new NewLineDelimitedMessageHandler(ClientPipe, formatter), + _ => throw new ArgumentOutOfRangeException() + }; RPC = new JsonRpc(handler, new JsonRPCPublicAPI(Context.API)); diff --git a/Flow.Launcher.Core/Plugin/NodePluginV2.cs b/Flow.Launcher.Core/Plugin/NodePluginV2.cs index d84a72864..c8cc37c57 100644 --- a/Flow.Launcher.Core/Plugin/NodePluginV2.cs +++ b/Flow.Launcher.Core/Plugin/NodePluginV2.cs @@ -24,5 +24,7 @@ namespace Flow.Launcher.Core.Plugin StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); await base.InitAsync(context); } + + protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.HeaderDelimited; } } diff --git a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs index f768daf15..5c36e0eea 100644 --- a/Flow.Launcher.Core/Plugin/PythonPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/PythonPluginV2.cs @@ -19,13 +19,10 @@ namespace Flow.Launcher.Core.Plugin internal sealed class PythonPluginV2 : ProcessStreamPluginV2 { protected override ProcessStartInfo StartInfo { get; set; } - + public PythonPluginV2(string filename) { - StartInfo = new ProcessStartInfo - { - FileName = filename, - }; + StartInfo = new ProcessStartInfo { FileName = filename, }; var path = Path.Combine(Constant.ProgramDirectory, JsonRpc); StartInfo.EnvironmentVariables["PYTHONPATH"] = path; @@ -33,11 +30,13 @@ namespace Flow.Launcher.Core.Plugin //Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable StartInfo.ArgumentList.Add("-B"); } - + public override async Task InitAsync(PluginInitContext context) { StartInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); await base.InitAsync(context); } + + protected override MessageHandlerType MessageHandler { get; } = MessageHandlerType.NewLineDelimited; } } From 38c96418332690f1332058c1ad4890878616e07f Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 19 Feb 2024 10:21:33 -0600 Subject: [PATCH 05/19] fix jsonrpcv2 plugin setting not sent --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 1ad6edc2c..74b7faa35 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -49,7 +49,7 @@ namespace Flow.Launcher.Core.Plugin try { var res = await RPC.InvokeWithCancellationAsync("query", - new[] { query }, + new object[] { query, Settings.Inner }, token); var results = ParseResults(res); From d3a05473b5fc7c917aaad7276de8f9d70df94ae3 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 19 Feb 2024 11:10:05 -0600 Subject: [PATCH 06/19] implement context_menu with JoinableTaskFactory --- Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs index 74b7faa35..46c72624a 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs @@ -39,9 +39,23 @@ namespace Flow.Launcher.Core.Plugin } } + private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext()); + public override List LoadContextMenus(Result selectedResult) { - throw new NotImplementedException(); + try + { + var res = JTF.Run(() => RPC.InvokeWithCancellationAsync("context_menu", + new object[] { selectedResult.ContextData })); + + var results = ParseResults(res); + + return results; + } + catch + { + return new List(); + } } public override async Task> QueryAsync(Query query, CancellationToken token) From 3ddf0cf6f6b5bbfdd6f080e49db509fdef94e12a Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sat, 24 Feb 2024 12:10:25 -0600 Subject: [PATCH 07/19] match longest shortcut by default --- Flow.Launcher/ViewModel/MainViewModel.cs | 43 ++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index db481d410..f41fd8dd8 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -176,7 +176,8 @@ namespace Flow.Launcher.ViewModel var token = e.Token == default ? _updateToken : e.Token; PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, token))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, + token))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } @@ -190,7 +191,8 @@ namespace Flow.Launcher.ViewModel Hide(); await PluginManager.ReloadDataAsync().ConfigureAwait(false); - Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); + Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), + InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); } [RelayCommand] @@ -265,6 +267,7 @@ namespace Flow.Launcher.ViewModel { autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}"; } + autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText; } @@ -286,11 +289,13 @@ namespace Flow.Launcher.ViewModel { results.SelectedIndex = int.Parse(index); } + var result = results.SelectedItem?.Result; if (result == null) { return; } + var hideWindow = await result.ExecuteAsync(new ActionContext { // not null means pressing modifier key + number, should ignore the modifier key @@ -403,6 +408,7 @@ namespace Flow.Launcher.ViewModel public bool GameModeStatus { get; set; } = false; private string _queryText; + public string QueryText { get => _queryText; @@ -426,6 +432,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowSize += 100; Settings.WindowLeft -= 50; } + OnPropertyChanged(); } @@ -441,6 +448,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowLeft += 50; Settings.WindowSize -= 100; } + OnPropertyChanged(); } @@ -520,18 +528,17 @@ namespace Flow.Launcher.ViewModel { if (QueryText != queryText) { - // re-query is done in QueryText's setter method QueryText = queryText; // set to false so the subsequent set true triggers // PropertyChanged and MoveQueryTextToEnd is called QueryTextCursorMovedToEnd = false; - } else if (isReQuery) { Query(isReQuery: true); } + QueryTextCursorMovedToEnd = true; }); } @@ -601,8 +608,8 @@ namespace Flow.Launcher.ViewModel public string OpenResultCommandModifiers => Settings.OpenResultModifiers; - public string PreviewHotkey - { + public string PreviewHotkey + { get { // TODO try to patch issue #1755 @@ -616,6 +623,7 @@ namespace Flow.Launcher.ViewModel { Settings.PreviewHotkey = "F1"; } + return Settings.PreviewHotkey; } } @@ -684,7 +692,6 @@ namespace Flow.Launcher.ViewModel results.Add(ContextMenuTopMost(selected)); results.Add(ContextMenuPluginInfo(selected.PluginID)); } - if (!string.IsNullOrEmpty(query)) @@ -703,7 +710,6 @@ namespace Flow.Launcher.ViewModel r.Score = match.Score; return true; - }).ToList(); ContextMenu.AddResults(filtered, id); } @@ -730,10 +736,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query - { - RawQuery = h.Query - }, + OriginQuery = new Query { RawQuery = h.Query }, Action = _ => { SelectedResults = Results; @@ -870,20 +873,23 @@ namespace Flow.Launcher.ViewModel // Task.Yield will force it to run in ThreadPool await Task.Yield(); - IReadOnlyList results = await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); + IReadOnlyList results = + await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); currentCancellationToken.ThrowIfCancellationRequested(); results ??= _emptyResult; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, + currentCancellationToken))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } } } - private Query ConstructQuery(string queryText, IEnumerable customShortcuts, IEnumerable builtInShortcuts) + private Query ConstructQuery(string queryText, IEnumerable customShortcuts, + IEnumerable builtInShortcuts) { if (string.IsNullOrWhiteSpace(queryText)) { @@ -893,7 +899,7 @@ namespace Flow.Launcher.ViewModel StringBuilder queryBuilder = new(queryText); StringBuilder queryBuilderTmp = new(queryText); - foreach (var shortcut in customShortcuts) + foreach (var shortcut in customShortcuts.OrderByDescending(x => x.Key.Length)) { if (queryBuilder.Equals(shortcut.Key)) { @@ -920,7 +926,9 @@ namespace Flow.Launcher.ViewModel } catch (Exception e) { - Log.Exception($"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", e); + Log.Exception( + $"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", + e); } } }); @@ -1065,6 +1073,7 @@ namespace Flow.Launcher.ViewModel { SelectedResults = Results; } + switch (Settings.LastQueryMode) { case LastQueryMode.Empty: From 56fb95ace36335b39ee01f71df61e3b7ba28cbaf Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 9 Mar 2024 23:03:42 +1100 Subject: [PATCH 08/19] New translations resources.resx (Chinese Simplified) [ci skip] --- .../Properties/Resources.zh-cn.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx index 8e71e09a3..2b39d48e4 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.zh-cn.resx @@ -1926,10 +1926,10 @@ 微软拼音 SimpleFast 选项 - Change what closing the lid does + 更改盖上盖子时的操作 - Turn off unnecessary animations + 关闭不必要的动画效果 创建还原点 From 10ebda8283679ca103b4585c98a951287cc11e42 Mon Sep 17 00:00:00 2001 From: asafmahlev Date: Mon, 18 Mar 2024 12:11:25 +0200 Subject: [PATCH 09/19] Added ctrl+shift+c --- Flow.Launcher/MainWindow.xaml | 4 ++++ Flow.Launcher/ViewModel/MainViewModel.cs | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 88e95aa69..e2c74d367 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -186,6 +186,10 @@ Key="F12" Command="{Binding ToggleGameModeCommand}" Modifiers="Ctrl" /> + Date: Mon, 18 Mar 2024 22:33:17 +0000 Subject: [PATCH 10/19] Bump Microsoft.NET.Test.Sdk from 17.8.0 to 17.9.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0 to 17.9.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.8.0...v17.9.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 29414baa6..fd967de4a 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -54,7 +54,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file From 8b45372f660066a1fd90ffd8b0f372ed59de15f2 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 22 Jan 2024 12:06:14 -0600 Subject: [PATCH 11/19] Use FastCache.Cached as new image cache instead of implement it ourselves --- .../Flow.Launcher.Infrastructure.csproj | 1 + .../Image/ImageCache.cs | 78 +++++++------------ .../Image/ImageLoader.cs | 11 ++- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index b24f069c1..574f3686a 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,6 +49,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 7a2b57637..55545b9a7 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Windows.Media; +using FastCache; +using FastCache.Services; namespace Flow.Launcher.Infrastructure.Image { - [Serializable] public class ImageUsage { - public int usage; public ImageSource imageSource; @@ -23,16 +23,13 @@ namespace Flow.Launcher.Infrastructure.Image public class ImageCache { - private const int MaxCached = 50; - public ConcurrentDictionary<(string, bool), ImageUsage> Data { get; } = new(); - private const int permissibleFactor = 2; - private SemaphoreSlim semaphore = new(1, 1); + private const int MaxCached = 150; public void Initialize(Dictionary<(string, bool), int> usage) { foreach (var key in usage.Keys) { - Data[key] = new ImageUsage(usage[key], null); + Cached.Save(key, new ImageUsage(usage[key], null), TimeSpan.MaxValue, MaxCached); } } @@ -40,70 +37,48 @@ namespace Flow.Launcher.Infrastructure.Image { get { - if (!Data.TryGetValue((path, isFullImage), out var value)) + if (!Cached.TryGet((path, isFullImage), out var value)) { return null; } - value.usage++; - return value.imageSource; + value.Value.usage++; + return value.Value.imageSource; } set { - Data.AddOrUpdate( - (path, isFullImage), - new ImageUsage(0, value), - (k, v) => - { - v.imageSource = value; - v.usage++; - return v; - } - ); - - SliceExtra(); - - async void SliceExtra() + if (Cached.TryGet((path, isFullImage), out var cached)) { - // To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size - // This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time - if (Data.Count > permissibleFactor * MaxCached) - { - await semaphore.WaitAsync().ConfigureAwait(false); - // To delete the images from the data dictionary based on the resizing of the Usage Dictionary - // Double Check to avoid concurrent remove - if (Data.Count > permissibleFactor * MaxCached) - foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key)) - Data.TryRemove(key, out _); - semaphore.Release(); - } + cached.Value.imageSource = value; + cached.Value.usage++; } + + Cached.Save((path, isFullImage), new ImageUsage(0, value), TimeSpan.MaxValue, + MaxCached); } } public bool ContainsKey(string key, bool isFullImage) { - return key is not null && Data.ContainsKey((key, isFullImage)) && Data[(key, isFullImage)].imageSource != null; + return Cached.TryGet((key, isFullImage), out _); } public bool TryGetValue(string key, bool isFullImage, out ImageSource image) { - if (key is not null) + if (Cached.TryGet((key, isFullImage), out var value)) { - bool hasKey = Data.TryGetValue((key, isFullImage), out var imageUsage); - image = hasKey ? imageUsage.imageSource : null; - return hasKey; - } - else - { - image = null; - return false; + image = value.Value.imageSource; + value.Value.usage++; + return image != null; } + + image = null; + return false; } public int CacheSize() { - return Data.Count; + return CacheManager.TotalCount<(string, bool), ImageUsage>(); } /// @@ -111,7 +86,14 @@ namespace Flow.Launcher.Infrastructure.Image /// public int UniqueImagesInCache() { - return Data.Values.Select(x => x.imageSource).Distinct().Count(); + return CacheManager.EnumerateEntries<(string, bool), ImageUsage>().Select(x => x.Value.imageSource) + .Distinct() + .Count(); + } + + public IEnumerable> EnumerateEntries() + { + return CacheManager.EnumerateEntries<(string, bool), ImageUsage>(); } } } diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index add6d4e92..75c2a4ec9 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -49,7 +49,7 @@ namespace Flow.Launcher.Infrastructure.Image { await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () => { - foreach (var ((path, isFullImage), _) in ImageCache.Data) + foreach (var ((path, isFullImage), _) in usage) { await LoadAsync(path, isFullImage); } @@ -65,7 +65,7 @@ namespace Flow.Launcher.Infrastructure.Image try { - _storage.SaveAsync(ImageCache.Data + await _storage.SaveAsync(ImageCache.EnumerateEntries() .ToDictionary( x => x.Key, x => x.Value.usage)); @@ -125,9 +125,12 @@ namespace Flow.Launcher.Infrastructure.Image return new ImageResult(MissingImage, ImageType.Error); } - if (ImageCache.ContainsKey(path, loadFullImage)) + // extra scope for use of same variable name { - return new ImageResult(ImageCache[path, loadFullImage], ImageType.Cache); + if (ImageCache.TryGetValue(path, loadFullImage, out var imageSource)) + { + return new ImageResult(imageSource, ImageType.Cache); + } } if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var uriResult) From 267163c8adc61aa339223e046205b00a79694949 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 22 Mar 2024 22:08:18 +0800 Subject: [PATCH 12/19] Update terms --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 2d6fdb7f0..c4b1ee849 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -106,3 +106,4 @@ alreadyexists JsonRPC JsonRPCV2 Softpedia +img From ca00a8fd47f5a79751b65dd96295a1e6955303ed Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 Mar 2024 03:28:14 +1100 Subject: [PATCH 13/19] New translations en.xaml (Dutch) [ci skip] --- Flow.Launcher/Languages/nl.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index 3a2ba44c1..5b28c5712 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -182,7 +182,7 @@ Query Shortcut Expansion - Description + Beschrijving Verwijder Bewerken Toevoegen From bf35e11c40528f11b1f78e1dfa47cdf60db9f02a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 Mar 2024 03:28:14 +1100 Subject: [PATCH 14/19] New translations en.xaml (Dutch) [ci skip] --- .../Languages/nl.xaml | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml index ca798f9d7..a71e28e98 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/nl.xaml @@ -2,8 +2,8 @@ - Command - Description + Opdracht + Beschrijving Shutdown Restart @@ -27,37 +27,37 @@ Toggle Game Mode - Shutdown Computer - Restart Computer - Restart the computer with Advanced Boot Options for Safe and Debugging modes, as well as other options - Log off - Lock this computer - Close Flow Launcher - Restart Flow Launcher + Computer afsluiten + Computer opnieuw opstarten + Start de computer opnieuw op met geavanceerde opstartopties voor veilige en foutopsporing modi en andere opties + Afmelden + Deze computer vergrendelen + Sluit Flow Launcher + Flow Launcher herstarten Tweak Flow Launcher's settings - Put computer to sleep - Empty recycle bin + De computer in slaapstand zetten + Prullenbak leegmaken Open recycle bin Indexing Options - Hibernate computer - Save all Flow Launcher settings - Refreshes plugin data with new content - Open Flow Launcher's log location - Check for new Flow Launcher update - Visit Flow Launcher's documentation for more help and how to use tips - Open the location where Flow Launcher's settings are stored + De computer in sluimerstand zetten + Sla alle Flow Launcher instellingen op + Vernieuwt plugin data met nieuwe inhoud + Open de locatie van Flow Launcher's log bestand + Controleer op nieuwe Flow Launcher update + Bezoek Flow Launcher's documentatie voor meer hulp en tips + Open de locatie waar Flow Launcher's instellingen worden opgeslagen Toggle Game Mode Succesvol - All Flow Launcher settings saved - Reloaded all applicable plugin data - Are you sure you want to shut the computer down? - Are you sure you want to restart the computer? - Are you sure you want to restart the computer with Advanced Boot Options? + Alle Flow Launcher instellingen opgeslagen + Alle toepasselijke plugin gegevens zijn herladen + Weet u zeker dat u de computer wilt uitschakelen? + Weet u zeker dat u de computer wilt herstarten? + Weet u zeker dat u de computer wilt herstarten met geavanceerde opstartopties? Are you sure you want to log off? - System Commands - Provides System related commands. e.g. shutdown, lock, settings etc. + Systeemopdrachten + Voorziet in systeem gerelateerde opdrachten. bijv.: afsluiten, vergrendelen, instellingen, enz. From d0a5ba501b39cfec5eb183557ed7b1d47efd5423 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 24 Mar 2024 03:28:16 +1100 Subject: [PATCH 15/19] New translations resources.resx (Dutch) [ci skip] --- .../Properties/Resources.nl-NL.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx index e5ff84248..c28489cc8 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Properties/Resources.nl-NL.resx @@ -456,7 +456,7 @@ Area Personalization - Command + Opdracht The command to direct start a setting From ab2f0bda2cfafc29959f7e4e8d2c4ff3eeab3dba Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 25 Mar 2024 21:20:27 +1100 Subject: [PATCH 16/19] New translations en.xaml (Russian) [ci skip] --- Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml index efb08c3f1..cb7f2cda5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml @@ -3,13 +3,13 @@ Сначала отметьте что-нибудь - Please select a folder link + Пожалуйста, выберите ссылку на папку Вы уверены, что хотите удалить {0}? Вы действительно хотите безвозвратно удалить этот файл? Вы действительно хотите безвозвратно удалить этот файл/папку? Удаление завершено Успешно удалено {0} - Assigning the global action keyword could bring up too many results during search. Please choose a specific action keyword + Назначение ключевого слова глобальных действий может привести к слишком большому количеству результатов поиска. Пожалуйста, выберите конкретное ключевое слово действий Quick Access can not be set to the global action keyword when enabled. Please choose a specific action keyword The required service for Windows Index Search does not appear to be running To fix this, start the Windows Search service. Select here to remove this warning From b759de47537e7ee4cd7892b488fa5a2c1426a542 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:37:24 +0800 Subject: [PATCH 17/19] Hide main window when dragging results. --- Flow.Launcher/ResultListBox.xaml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index 78720e86a..ac51b195c 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Windows; using System.Windows.Controls; @@ -137,6 +137,8 @@ namespace Flow.Launcher isDragging = false; + App.API.HideMainWindow(); + var data = new DataObject(DataFormats.FileDrop, new[] { path From 3658f1537e8a9feb4feeca92209dd8effab8aed8 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 Mar 2024 04:47:58 +1100 Subject: [PATCH 18/19] New translations en.xaml (Slovak) [ci skip] --- Flow.Launcher/Languages/sk.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index 91ae87631..252bfb81d 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -171,7 +171,7 @@ Klávesová skratka pre Flow Launcher Zadajte skratku na zobrazenie/skrytie Flow Launchera. Klávesová skratka pre náhľad - Zadajte klávesovú skratku pre zobrazenie/skytie náhľadu vo vyhľadávacom okne. + Zadajte klávesovú skratku pre zobrazenie/skrytie náhľadu vo vyhľadávacom okne. Modifikačný kláves na otvorenie výsledkov Vyberte modifikačný kláves na otvorenie vybraného výsledku pomocou klávesnice. Zobraziť klávesovú skratku From d8d006fee9ccdd390598b3b910b9f1bf02db89fe Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 27 Mar 2024 13:56:10 +1100 Subject: [PATCH 19/19] add comment --- Flow.Launcher/ViewModel/MainViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index f41fd8dd8..58d5f1de0 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -899,6 +899,7 @@ namespace Flow.Launcher.ViewModel StringBuilder queryBuilder = new(queryText); StringBuilder queryBuilderTmp = new(queryText); + // Sorting order is important here, the reason is for matching longest shortcut by default foreach (var shortcut in customShortcuts.OrderByDescending(x => x.Key.Length)) { if (queryBuilder.Equals(shortcut.Key))