From b4c61927f0d2324379c3fc3e498b2a11c44e26ae Mon Sep 17 00:00:00 2001 From: Alekhya Reddy Date: Wed, 24 Jun 2020 12:45:31 +1000 Subject: [PATCH 001/398] Remove Program Plugin dll (ShObjldITIb.dll) --- .../Programs/ApplicationActivationHelper.cs | 44 + .../Programs/ShellLinkHelper.cs | 132 +++ .../Programs/UWP.cs | 5 +- .../Programs/Win32.cs | 32 +- .../ShObjIdlTlb.dll | Bin 144896 -> 0 bytes installer/PowerToysSetup/Product.wxs | 937 ++++++++++++++++++ 6 files changed, 1129 insertions(+), 21 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs delete mode 100644 Plugins/Flow.Launcher.Plugin.Program/ShObjIdlTlb.dll create mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs new file mode 100644 index 000000000..989995f31 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace Microsoft.Plugin.Program.Programs +{ + public class ApplicationActivationHelper + { + // Reference : https://github.com/MicrosoftEdge/edge-launcher/blob/108e63df0b4cb5cd9d5e45aa7a264690851ec51d/MIcrosoftEdgeLauncherCsharp/Program.cs + public enum ActivateOptions + { + None = 0x00000000, + DesignMode = 0x00000001, + NoErrorUI = 0x00000002, + NoSplashScreen = 0x00000004, + } + + /// ApplicationActivationManager + [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IApplicationActivationManager + { + IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); + IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); + IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + } + + // Application Activation Manager Class + [ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] + public class ApplicationActivationManager : IApplicationActivationManager + { + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] + public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public extern IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public extern IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs new file mode 100644 index 000000000..49977393a --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.IO; +using Accessibility; +using System.Runtime.InteropServices.ComTypes; +using System.Security.Policy; + +namespace Microsoft.Plugin.Program.Programs +{ + class ShellLinkHelper + { + [Flags()] + public enum SLGP_FLAGS + { + SLGP_SHORTPATH = 0x1, + SLGP_UNCPRIORITY = 0x2, + SLGP_RAWPATH = 0x4 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct WIN32_FIND_DATAW + { + public uint dwFileAttributes; + public long ftCreationTime; + public long ftLastAccessTime; + public long ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + + [Flags()] + public enum SLR_FLAGS + { + SLR_NO_UI = 0x1, + SLR_ANY_MATCH = 0x2, + SLR_UPDATE = 0x4, + SLR_NOUPDATE = 0x8, + SLR_NOSEARCH = 0x10, + SLR_NOTRACK = 0x20, + SLR_NOLINKINFO = 0x40, + SLR_INVOKE_MSI = 0x80 + } + + + // Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW + /// The IShellLink interface allows Shell links to be created, modified, and resolved + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + interface IShellLinkW + { + /// Retrieves the path and file name of a Shell link object + void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); + /// Retrieves the list of item identifiers for a Shell link object + void GetIDList(out IntPtr ppidl); + /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + void SetIDList(IntPtr pidl); + /// Retrieves the description string for a Shell link object + void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + /// Sets the description for a Shell link object. The description can be any application-defined string + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + /// Retrieves the name of the working directory for a Shell link object + void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + /// Sets the name of the working directory for a Shell link object + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + /// Retrieves the command-line arguments associated with a Shell link object + void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + /// Sets the command-line arguments for a Shell link object + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + /// Retrieves the hot key for a Shell link object + void GetHotkey(out short pwHotkey); + /// Sets a hot key for a Shell link object + void SetHotkey(short wHotkey); + /// Retrieves the show command for a Shell link object + void GetShowCmd(out int piShowCmd); + /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + void SetShowCmd(int iShowCmd); + /// Retrieves the location (path and index) of the icon for a Shell link object + void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + /// Sets the location (path and index) of the icon for a Shell link object + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + /// Sets the relative path to the Shell link object + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + /// Attempts to find the target of a Shell link, even if it has been moved or renamed + void Resolve(ref Accessibility._RemotableHandle hwnd, SLR_FLAGS fFlags); + /// Sets the path and file name of a Shell link object + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + [ComImport(), Guid("00021401-0000-0000-C000-000000000046")] + public class ShellLink + { + } + + // To initialize the app description + public String description = String.Empty; + + + // Retrieve the target path using Shell Link + public string retrieveTargetPath(string path) + { + var link = new ShellLink(); + const int STGM_READ = 0; + ((IPersistFile)link).Load(path, STGM_READ); + var hwnd = new _RemotableHandle(); + ((IShellLinkW)link).Resolve(ref hwnd, 0); + + const int MAX_PATH = 260; + StringBuilder buffer = new StringBuilder(MAX_PATH); + + var data = new WIN32_FIND_DATAW(); + ((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH); + var target = buffer.ToString(); + + // To set the app description + if (!String.IsNullOrEmpty(target)) + { + buffer = new StringBuilder(MAX_PATH); + ((IShellLinkW)link).GetDescription(buffer, MAX_PATH); + description = buffer.ToString(); + } + return target; + } + } +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index b8633f357..173cedd8d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -13,7 +13,6 @@ using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; using AppxPackaing; -using Shell; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.Program.Logger; using IStream = AppxPackaing.IStream; @@ -337,10 +336,10 @@ namespace Flow.Launcher.Plugin.Program.Programs private async void Launch(IPublicAPI api) { - var appManager = new ApplicationActivationManager(); + var appManager = new ApplicationActivationHelper.ApplicationActivationManager(); uint unusedPid; const string noArgs = ""; - const ACTIVATEOPTIONS noFlags = ACTIVATEOPTIONS.AO_NONE; + const ApplicationActivationHelper.ActivateOptions noFlags = ApplicationActivationHelper.ActivateOptions.None; await Task.Run(() => { try diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index cdea767f3..3b1da3abd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -8,13 +8,14 @@ using System.Security; using System.Text; using System.Threading.Tasks; using Microsoft.Win32; -using Shell; -using Flow.Launcher.Infrastructure; -using Flow.Launcher.Plugin.Program.Logger; -using Flow.Launcher.Plugin.SharedCommands; +using Wox.Infrastructure; +using Microsoft.Plugin.Program.Logger; +using Wox.Plugin; +using System.Reflection; -namespace Flow.Launcher.Plugin.Program.Programs +namespace Microsoft.Plugin.Program.Programs { + [Serializable] public class Win32 : IProgram { @@ -190,27 +191,21 @@ namespace Flow.Launcher.Plugin.Program.Programs var program = Win32Program(path); try { - var link = new ShellLink(); - const uint STGM_READ = 0; - ((IPersistFile)link).Load(path, STGM_READ); - var hwnd = new _RemotableHandle(); - link.Resolve(ref hwnd, 0); - const int MAX_PATH = 260; StringBuilder buffer = new StringBuilder(MAX_PATH); + ShellLinkHelper _helper = new ShellLinkHelper(); + string target = _helper.retrieveTargetPath(path); - var data = new _WIN32_FIND_DATAW(); - const uint SLGP_SHORTPATH = 1; - link.GetPath(buffer, buffer.Capacity, ref data, SLGP_SHORTPATH); - var target = buffer.ToString(); if (!string.IsNullOrEmpty(target)) { var extension = Extension(target); if (extension == ExeExtension && File.Exists(target)) { - buffer = new StringBuilder(MAX_PATH); - link.GetDescription(buffer, MAX_PATH); - var description = buffer.ToString(); + program.LnkResolvedPath = program.FullPath; + program.FullPath = Path.GetFullPath(target).ToLower(); + program.ExecutableName = Path.GetFileName(target); + + var description = _helper.description; if (!string.IsNullOrEmpty(description)) { program.Description = description; @@ -276,6 +271,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var files = new List(); var folderQueue = new Queue(); folderQueue.Enqueue(directory); + do { var currentDirectory = folderQueue.Dequeue(); diff --git a/Plugins/Flow.Launcher.Plugin.Program/ShObjIdlTlb.dll b/Plugins/Flow.Launcher.Plugin.Program/ShObjIdlTlb.dll deleted file mode 100644 index 83815e40a4380f97638c8c0af35427eb4ae808e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144896 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
#2?NVUwg`D`1PSuV5rEjE7A#&LPJA1M7~+PKWd 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 D=|-o! diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs new file mode 100644 index 000000000..868acfbf1 --- /dev/null +++ b/installer/PowerToysSetup/Product.wxs @@ -0,0 +1,937 @@ + + + + + + + + + + + + + + + + + + + + + + = 17134)]]> + + + + + + + + + + + + + + + NOT Installed + 1 + NOT Installed + NOT Installed + Installed AND _REMOVE_ALL="Yes" + Installed AND _REMOVE_ALL="Yes" + Installed AND NOT (_REMOVE_ALL="Yes") + Installed AND NOT (_REMOVE_ALL="Yes") + + + + + + + + + + + + + + + + + + + + + + NOT Installed and CREATESCHEDULEDTASK = 1 + + + + + NOT Installed + + + Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") + + + + NOT Installed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSTALLDESKTOPSHORTCUT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4f8ca9a453033187a71147758d378f4737d09d01 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 24 Jun 2020 12:55:21 +1000 Subject: [PATCH 002/398] update commit for Flow-Launcher --- .../Programs/ApplicationActivationHelper.cs | 2 +- .../Programs/ShellLinkHelper.cs | 2 +- .../Programs/Win32.cs | 10 +- installer/PowerToysSetup/Product.wxs | 937 ------------------ 4 files changed, 7 insertions(+), 944 deletions(-) delete mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs index 989995f31..3023836c1 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.RegularExpressions; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { public class ApplicationActivationHelper { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs index 49977393a..4ded3412a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs @@ -7,7 +7,7 @@ using Accessibility; using System.Runtime.InteropServices.ComTypes; using System.Security.Policy; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { class ShellLinkHelper { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 3b1da3abd..f9fcc0897 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -8,12 +8,11 @@ using System.Security; using System.Text; using System.Threading.Tasks; using Microsoft.Win32; -using Wox.Infrastructure; -using Microsoft.Plugin.Program.Logger; -using Wox.Plugin; -using System.Reflection; +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Plugin.Program.Logger; +using Flow.Launcher.Plugin.SharedCommands; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { [Serializable] @@ -23,6 +22,7 @@ namespace Microsoft.Plugin.Program.Programs public string UniqueIdentifier { get; set; } public string IcoPath { get; set; } public string FullPath { get; set; } + public string LnkResolvedPath { get; set; } public string ParentDirectory { get; set; } public string ExecutableName { get; set; } public string Description { get; set; } diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs deleted file mode 100644 index 868acfbf1..000000000 --- a/installer/PowerToysSetup/Product.wxs +++ /dev/null @@ -1,937 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - = 17134)]]> - - - - - - - - - - - - - - - NOT Installed - 1 - NOT Installed - NOT Installed - Installed AND _REMOVE_ALL="Yes" - Installed AND _REMOVE_ALL="Yes" - Installed AND NOT (_REMOVE_ALL="Yes") - Installed AND NOT (_REMOVE_ALL="Yes") - - - - - - - - - - - - - - - - - - - - - - NOT Installed and CREATESCHEDULEDTASK = 1 - - - - - NOT Installed - - - Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") - - - - NOT Installed - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INSTALLDESKTOPSHORTCUT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 0342693442d5cce41c1ebd8537adbe9504116bbd Mon Sep 17 00:00:00 2001 From: Alekhya Reddy Date: Wed, 24 Jun 2020 21:34:33 +1000 Subject: [PATCH 003/398] Remove Program Plugin dll (AppxPackagingTlb.dll) --- .../AppxPackagingTlb.dll | Bin 17408 -> 0 bytes .../Flow.Launcher.Plugin.Program.csproj | 11 - .../Programs/AppxPackageHelper.cs | 82 ++ .../Programs/UWP.cs | 69 +- installer/PowerToysSetup/Product.wxs | 951 ++++++++++++++++++ 5 files changed, 1076 insertions(+), 37 deletions(-) delete mode 100644 Plugins/Flow.Launcher.Plugin.Program/AppxPackagingTlb.dll create mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs create mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/AppxPackagingTlb.dll b/Plugins/Flow.Launcher.Plugin.Program/AppxPackagingTlb.dll deleted file mode 100644 index 183cfc085c378e7b1927d3354d65d7206e15c978..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17408 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 HW#InPreserveNewest - - - - .\AppxPackagingTlb.dll - True - - - .\ShObjIdlTlb.dll - True - - diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs new file mode 100644 index 000000000..f9605a574 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using Windows.Storage; + +namespace Microsoft.Plugin.Program.Programs +{ + public class AppxPackageHelper + { + // This function returns a list of attributes of applications + public List getAppsFromManifest(IStream stream) + { + List apps = new List(); + var appxFactory = new AppxFactory(); + var reader = ((IAppxFactory)appxFactory).CreateManifestReader(stream); + var manifestApps = reader.GetApplications(); + while (manifestApps.GetHasCurrent()) + { + string appListEntry; + var manifestApp = manifestApps.GetCurrent(); + manifestApp.GetStringValue("AppListEntry", out appListEntry); + if (appListEntry != "none") + { + apps.Add(manifestApp); + } + manifestApps.MoveNext(); + } + return apps; + } + + // Reference : https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application + [Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781"), ComImport] + public class AppxFactory + { + } + + [Guid("BEB94909-E451-438B-B5A7-D79E767B75D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxFactory + { + void _VtblGap0_2(); // skip 2 methods + IAppxManifestReader CreateManifestReader(IStream inputStream); + } + + [Guid("4E1BD148-55A0-4480-A3D1-15544710637C"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestReader + { + void _VtblGap0_1(); // skip 1 method + IAppxManifestProperties GetProperties(); + void _VtblGap1_5(); // skip 5 methods + IAppxManifestApplicationsEnumerator GetApplications(); + } + + [Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestApplicationsEnumerator + { + IAppxManifestApplication GetCurrent(); + bool GetHasCurrent(); + bool MoveNext(); + } + + [Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestApplication + { + [PreserveSig] + int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); + + [PreserveSig] + int GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value); + } + + [Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAppxManifestProperties + { + [PreserveSig] + int GetBoolValue([MarshalAs(UnmanagedType.LPWStr)]string name, out bool value); + [PreserveSig] + int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index b8633f357..1706bb606 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -12,14 +12,18 @@ using System.Windows.Media.Imaging; using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; -using AppxPackaing; -using Shell; -using Flow.Launcher.Infrastructure; -using Flow.Launcher.Plugin.Program.Logger; -using IStream = AppxPackaing.IStream; +using Wox.Infrastructure; +using Microsoft.Plugin.Program.Logger; using Rect = System.Windows.Rect; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Media; +using System.Windows.Controls; +using Wox.Plugin; +using System.Reflection; +using Wox.Plugin.SharedCommands; +using System.Runtime.InteropServices.ComTypes; -namespace Flow.Launcher.Plugin.Program.Programs +namespace Microsoft.Plugin.Program.Programs { [Serializable] public class UWP @@ -51,12 +55,12 @@ namespace Flow.Launcher.Plugin.Program.Programs private void InitializeAppInfo() { + AppxPackageHelper _helper = new AppxPackageHelper(); var path = Path.Combine(Location, "AppxManifest.xml"); var namespaces = XmlNamespaces(path); InitPackageVersion(namespaces); - var appxFactory = new AppxFactory(); IStream stream; const uint noAttribute = 0x80; const Stgm exclusiveRead = Stgm.Read | Stgm.ShareExclusive; @@ -64,20 +68,15 @@ namespace Flow.Launcher.Plugin.Program.Programs if (hResult == Hresult.Ok) { - var reader = appxFactory.CreateManifestReader(stream); - var manifestApps = reader.GetApplications(); var apps = new List(); - while (manifestApps.GetHasCurrent() != 0) + + List _apps = _helper.getAppsFromManifest(stream); + foreach(var _app in _apps) { - var manifestApp = manifestApps.GetCurrent(); - var appListEntry = manifestApp.GetStringValue("AppListEntry"); - if (appListEntry != "none") - { - var app = new Application(manifestApp, this); - apps.Add(app); - } - manifestApps.MoveNext(); + var app = new Application(_app, this); + apps.Add(app); } + Apps = apps.Where(a => a.AppListEntry != "none").ToArray(); } else @@ -356,13 +355,30 @@ namespace Flow.Launcher.Plugin.Program.Programs }); } - public Application(IAppxManifestApplication manifestApp, UWP package) + public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP package) { - UserModelId = manifestApp.GetAppUserModelId(); - UniqueIdentifier = manifestApp.GetAppUserModelId(); - DisplayName = manifestApp.GetStringValue("DisplayName"); - Description = manifestApp.GetStringValue("Description"); - BackgroundColor = manifestApp.GetStringValue("BackgroundColor"); + // This is done because we cannot use the keyword 'out' along with a property + string tmpUserModelId; + string tmpUniqueIdentifier; + string tmpDisplayName; + string tmpDescription; + string tmpBackgroundColor; + string tmpEntryPoint; + + manifestApp.GetAppUserModelId(out tmpUserModelId); + manifestApp.GetAppUserModelId(out tmpUniqueIdentifier); + manifestApp.GetStringValue("DisplayName", out tmpDisplayName); + manifestApp.GetStringValue("Description", out tmpDescription); + manifestApp.GetStringValue("BackgroundColor", out tmpBackgroundColor); + manifestApp.GetStringValue("EntryPoint", out tmpEntryPoint); + + UserModelId = tmpUserModelId; + UniqueIdentifier = tmpUniqueIdentifier; + DisplayName = tmpDisplayName; + Description = tmpDescription; + BackgroundColor = tmpBackgroundColor; + EntryPoint = tmpEntryPoint; + Package = package; DisplayName = ResourceFromPri(package.FullName, package.Name, DisplayName); @@ -430,7 +446,7 @@ namespace Flow.Launcher.Plugin.Program.Programs return $"{prefix}//{packageName}{key}"; } - internal string LogoUriFromManifest(IAppxManifestApplication app) + internal string LogoUriFromManifest(AppxPackageHelper.IAppxManifestApplication app) { var logoKeyFromVersion = new Dictionary { @@ -440,8 +456,9 @@ namespace Flow.Launcher.Plugin.Program.Programs }; if (logoKeyFromVersion.ContainsKey(Package.Version)) { + string logoUri; var key = logoKeyFromVersion[Package.Version]; - var logoUri = app.GetStringValue(key); + app.GetStringValue(key, out logoUri); return logoUri; } else diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs new file mode 100644 index 000000000..2ff4c033f --- /dev/null +++ b/installer/PowerToysSetup/Product.wxs @@ -0,0 +1,951 @@ + + + + + + + + + + + + + + + + + + + + + + = 17134)]]> + + + + + + + + + + + + + + + NOT Installed + 1 + NOT Installed + NOT Installed + Installed AND _REMOVE_ALL="Yes" + Installed AND _REMOVE_ALL="Yes" + Installed AND NOT (_REMOVE_ALL="Yes") + Installed AND NOT (_REMOVE_ALL="Yes") + + + + + + + + + + + + + + + + + + + + + + NOT Installed and CREATESCHEDULEDTASK = 1 + + + + + NOT Installed + + + Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") + + + + NOT Installed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSTALLDESKTOPSHORTCUT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 327e2f32531afdffd9262a1970773a7055b5b6e0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 24 Jun 2020 21:55:52 +1000 Subject: [PATCH 004/398] update commit for Flow-Launcher --- .../Programs/AppxPackageHelper.cs | 2 +- .../Programs/UWP.cs | 15 +- installer/PowerToysSetup/Product.wxs | 951 ------------------ 3 files changed, 6 insertions(+), 962 deletions(-) delete mode 100644 installer/PowerToysSetup/Product.wxs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs index f9605a574..e48f30757 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Windows.Storage; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { public class AppxPackageHelper { diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 1706bb606..6818a8bcd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Security.Principal; using System.Text; using System.Threading.Tasks; @@ -12,18 +13,11 @@ using System.Windows.Media.Imaging; using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; -using Wox.Infrastructure; -using Microsoft.Plugin.Program.Logger; +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Plugin.Program.Logger; using Rect = System.Windows.Rect; -using Windows.UI.Xaml.Media.Imaging; -using Windows.UI.Xaml.Media; -using System.Windows.Controls; -using Wox.Plugin; -using System.Reflection; -using Wox.Plugin.SharedCommands; -using System.Runtime.InteropServices.ComTypes; -namespace Microsoft.Plugin.Program.Programs +namespace Flow.Launcher.Plugin.Program.Programs { [Serializable] public class UWP @@ -253,6 +247,7 @@ namespace Microsoft.Plugin.Program.Programs public string UserModelId { get; set; } public string BackgroundColor { get; set; } + public string EntryPoint { get; set; } public string Name => DisplayName; public string Location => Package.Location; diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs deleted file mode 100644 index 2ff4c033f..000000000 --- a/installer/PowerToysSetup/Product.wxs +++ /dev/null @@ -1,951 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - = 17134)]]> - - - - - - - - - - - - - - - NOT Installed - 1 - NOT Installed - NOT Installed - Installed AND _REMOVE_ALL="Yes" - Installed AND _REMOVE_ALL="Yes" - Installed AND NOT (_REMOVE_ALL="Yes") - Installed AND NOT (_REMOVE_ALL="Yes") - - - - - - - - - - - - - - - - - - - - - - NOT Installed and CREATESCHEDULEDTASK = 1 - - - - - NOT Installed - - - Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") - - - - NOT Installed - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EXISTINGPOWERRENAMEEXTPATH OR EXISTINGIMAGERESIZERPATH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INSTALLDESKTOPSHORTCUT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 9c7dc054defe73dbdd917e16ef7d341be1dfaa5a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 25 Jun 2020 06:34:30 +1000 Subject: [PATCH 005/398] remove reference of dll in project file --- .../Flow.Launcher.Plugin.Program.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 331566f90..fb3a894db 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -44,10 +44,6 @@ .\AppxPackagingTlb.dll True - - .\ShObjIdlTlb.dll - True - From b52dbbea53e7181e4eef49aeccbe7d3ab5de7d6e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 19 Aug 2020 21:02:46 +1000 Subject: [PATCH 006/398] launch win32 lnk programs using LnkResolvedPath use LnkResolvedPath which is the original lnk path, rather than using the FullPath property which is assigned with the path to the actual exe --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f9fcc0897..f0de9f2c2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -54,7 +54,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var result = new Result { - SubTitle = FullPath, + SubTitle = LnkResolvedPath ?? FullPath, IcoPath = IcoPath, Score = score, ContextData = this, @@ -62,7 +62,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { var info = new ProcessStartInfo { - FileName = FullPath, + FileName = LnkResolvedPath ?? FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true }; From d3e88c478a19e2638d461676f3d6648269b2a9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 19 Dec 2020 14:09:16 +0800 Subject: [PATCH 007/398] Add Programs Hasher and use IEnumerable instead of ParallelQuery since it performs better. --- .../Programs/Win32.cs | 81 +++++++++++++------ 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 2441b59f4..0f976cd58 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -11,6 +10,9 @@ using Microsoft.Win32; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.Program.Logger; using Flow.Launcher.Plugin.SharedCommands; +using Flow.Launcher.Infrastructure.Logger; +using System.Diagnostics; +using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program.Programs { @@ -53,6 +55,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var result = new Result { + Title = title, SubTitle = LnkResolvedPath ?? FullPath, IcoPath = IcoPath, Score = matchResult.Score, @@ -262,10 +265,10 @@ namespace Flow.Launcher.Plugin.Program.Programs try { var paths = Directory.EnumerateFiles(directory, "*", new EnumerationOptions - { - IgnoreInaccessible = true, - RecurseSubdirectories = true - }) + { + IgnoreInaccessible = true, + RecurseSubdirectories = true + }) .Where(x => suffixes.Contains(Extension(x))); return paths; @@ -295,14 +298,14 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - private static ParallelQuery UnregisteredPrograms(List sources, string[] suffixes) + private static IEnumerable UnregisteredPrograms(List sources, string[] suffixes) { var paths = sources.Where(s => Directory.Exists(s.Location) && s.Enabled) .SelectMany(s => ProgramPaths(s.Location, suffixes)) .Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier)) .Distinct(); - var programs = paths.AsParallel().Select(x => Extension(x) switch + var programs = paths.Select(x => Extension(x) switch { ExeExtension => ExeProgram(x), ShortcutExtension => LnkProgram(x), @@ -313,7 +316,7 @@ namespace Flow.Launcher.Plugin.Program.Programs return programs; } - private static ParallelQuery StartMenuPrograms(string[] suffixes) + private static IEnumerable StartMenuPrograms(string[] suffixes) { var disabledProgramsList = Main._settings.DisabledProgramSources; @@ -325,7 +328,6 @@ namespace Flow.Launcher.Plugin.Program.Programs var toFilter = paths1.Concat(paths2); var programs = toFilter - .AsParallel() .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) .Distinct() .Select(x => Extension(x) switch @@ -336,32 +338,31 @@ namespace Flow.Launcher.Plugin.Program.Programs return programs; } - private static ParallelQuery AppPathsPrograms(string[] suffixes) + private static IEnumerable AppPathsPrograms(string[] suffixes) { // https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"; - var programs = new List(); - using (var root = Registry.LocalMachine.OpenSubKey(appPaths)) + IEnumerable programs = Enumerable.Empty(); + + using var rootMachine = Registry.LocalMachine.OpenSubKey(appPaths); + using var rootUser = Registry.CurrentUser.OpenSubKey(appPaths); + + if (rootMachine != null) { - if (root != null) - { - programs.AddRange(GetProgramsFromRegistry(root)); - } + programs = programs.Concat(GetProgramsFromRegistry(rootMachine)); } - using (var root = Registry.CurrentUser.OpenSubKey(appPaths)) + if (rootUser != null) { - if (root != null) - { - programs.AddRange(GetProgramsFromRegistry(root)); - } + programs = programs.Concat(GetProgramsFromRegistry(rootUser)); } + var disabledProgramsList = Main._settings.DisabledProgramSources; - var toFilter = programs.AsParallel().Where(p => suffixes.Contains(Extension(p.ExecutableName))); + var toFilter = programs.Where(p => suffixes.Contains(Extension(p.ExecutableName))); var filtered = toFilter.Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)).Select(t1 => t1); - return filtered; + return filtered.ToList(); } private static IEnumerable GetProgramsFromRegistry(RegistryKey root) @@ -418,14 +419,43 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } + private class Win32ComparatorBasedonDescription : IEqualityComparer + { + public readonly static Win32ComparatorBasedonDescription Default = new Win32ComparatorBasedonDescription(); + + public bool Equals(Win32 x, Win32 y) + { + return x.Description == y.Description; + } + + public int GetHashCode(Win32 obj) + { + return obj.Description.GetHashCode(); + } + } + + private static Win32[] ProgramsHasher(IEnumerable programs) + => programs.GroupBy(p => p.FullPath.ToLower()) + .SelectMany(g => + { + var tempList = g.ToList(); + if (tempList.Count() > 1) + return g.Where(p => !string.IsNullOrEmpty(p.Description)) + .Distinct(Win32ComparatorBasedonDescription.Default); + else + return g.Take(1); + }).ToArray(); + + public static Win32[] All(Settings settings) { try { - var programs = new List().AsParallel(); + var programs = Enumerable.Empty(); var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes); programs = programs.Concat(unregistered); + if (settings.EnableRegistrySource) { var appPaths = AppPathsPrograms(settings.ProgramSuffixes); @@ -437,8 +467,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var startMenu = StartMenuPrograms(settings.ProgramSuffixes); programs = programs.Concat(startMenu); } - - return programs.ToArray(); + return ProgramsHasher(programs); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. catch (Exception e) From f693f1c6affba6f7ab466565b10ccde2eb07258c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 20 Dec 2020 17:25:14 +0800 Subject: [PATCH 008/398] use Array.empty instead of new [0] to avoid unused allocation --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 0f976cd58..e7a7c2bd7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -481,7 +481,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { ProgramLogger.LogException("|Win32|All|Not available|An unexpected error occurred", e); - return new Win32[0]; + return Array.Empty(); } #endif } From 12891a1888368164c4ff807565a704d7396db9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 9 Jan 2021 11:30:11 +0800 Subject: [PATCH 009/398] change publish profile to Net5 one --- Scripts/post_build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index b08fac8f6..58d5c6a4e 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -90,7 +90,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) { function Publish-Self-Contained ($p) { $csproj = Join-Path "$p" "Flow.Launcher/Flow.Launcher.csproj" -Resolve - $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/NetCore3.1-SelfContained.pubxml" -Resolve + $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/Net5-SelfContained.pubxml" -Resolve # we call dotnet publish on the main project. # The other projects should have been built in Release at this point. From d35d7ae2d6891bcb074fc58849a047513ff8cb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sun, 17 Jan 2021 23:45:15 +0800 Subject: [PATCH 010/398] Remove unused catch and optimize code --- .../Programs/Win32.cs | 119 ++++++++---------- 1 file changed, 54 insertions(+), 65 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index e7a7c2bd7..b6ca7ffcc 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -16,7 +16,6 @@ using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program.Programs { - [Serializable] public class Win32 : IProgram { @@ -127,19 +126,19 @@ namespace Flow.Launcher.Plugin.Program.Programs Action = _ => { var args = !string.IsNullOrWhiteSpace(Main._settings.CustomizedArgs) - ? Main._settings.CustomizedArgs - .Replace("%s",$"\"{ParentDirectory}\"") - .Replace("%f",$"\"{FullPath}\"") - : Main._settings.CustomizedExplorer==Settings.Explorer - ? $"/select,\"{FullPath}\"" - : Settings.ExplorerArgs; + ? Main._settings.CustomizedArgs + .Replace("%s", $"\"{ParentDirectory}\"") + .Replace("%f", $"\"{FullPath}\"") + : Main._settings.CustomizedExplorer == Settings.Explorer + ? $"/select,\"{FullPath}\"" + : Settings.ExplorerArgs; Main.StartProcess(Process.Start, - new ProcessStartInfo( - !string.IsNullOrWhiteSpace(Main._settings.CustomizedExplorer) - ? Main._settings.CustomizedExplorer - : Settings.Explorer, - args)); + new ProcessStartInfo( + !string.IsNullOrWhiteSpace(Main._settings.CustomizedExplorer) + ? Main._settings.CustomizedExplorer + : Settings.Explorer, + args)); return true; }, @@ -150,7 +149,6 @@ namespace Flow.Launcher.Plugin.Program.Programs } - public override string ToString() { return ExecutableName; @@ -176,9 +174,9 @@ namespace Flow.Launcher.Plugin.Program.Programs catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { ProgramLogger.LogException($"|Win32|Win32Program|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() { Valid = false, Enabled = false }; + return new Win32() {Valid = false, Enabled = false}; } } @@ -216,13 +214,15 @@ namespace Flow.Launcher.Plugin.Program.Programs } } } + return program; } catch (COMException e) { // C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\MiracastView.lnk always cause exception ProgramLogger.LogException($"|Win32|LnkProgram|{path}" + - "|Error caused likely due to trying to get the description of the program", e); + "|Error caused likely due to trying to get the description of the program", + e); program.Valid = false; return program; @@ -252,37 +252,22 @@ namespace Flow.Launcher.Plugin.Program.Programs catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { ProgramLogger.LogException($"|Win32|ExeProgram|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() { Valid = false, Enabled = false }; + return new Win32() {Valid = false, Enabled = false}; } } private static IEnumerable ProgramPaths(string directory, string[] suffixes) { if (!Directory.Exists(directory)) - return new string[] { }; - try - { - var paths = Directory.EnumerateFiles(directory, "*", new EnumerationOptions - { - IgnoreInaccessible = true, - RecurseSubdirectories = true - }) - .Where(x => suffixes.Contains(Extension(x))); + return Enumerable.Empty(); - return paths; - } - catch (DirectoryNotFoundException e) + return Directory.EnumerateFiles(directory, "*", new EnumerationOptions { - ProgramLogger.LogException($"Directory not found {directory}", e); - return new string[] { }; - } - catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) - { - ProgramLogger.LogException($"Permission denied {directory}", e); - return new string[] { }; - } + IgnoreInaccessible = true, + RecurseSubdirectories = true + }).Where(x => suffixes.Contains(Extension(x))); } private static string Extension(string path) @@ -328,13 +313,13 @@ namespace Flow.Launcher.Plugin.Program.Programs var toFilter = paths1.Concat(paths2); var programs = toFilter - .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) - .Distinct() - .Select(x => Extension(x) switch - { - ShortcutExtension => LnkProgram(x), - _ => Win32Program(x) - }).Where(x => x.Valid); + .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) + .Distinct() + .Select(x => Extension(x) switch + { + ShortcutExtension => LnkProgram(x), + _ => Win32Program(x) + }).Where(x => x.Valid); return programs; } @@ -351,6 +336,7 @@ namespace Flow.Launcher.Plugin.Program.Programs { programs = programs.Concat(GetProgramsFromRegistry(rootMachine)); } + if (rootUser != null) { programs = programs.Concat(GetProgramsFromRegistry(rootUser)); @@ -360,7 +346,9 @@ namespace Flow.Launcher.Plugin.Program.Programs var disabledProgramsList = Main._settings.DisabledProgramSources; var toFilter = programs.Where(p => suffixes.Contains(Extension(p.ExecutableName))); - var filtered = toFilter.Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)).Select(t1 => t1); + var filtered = toFilter + .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) + .Select(t1 => t1); return filtered.ToList(); } @@ -368,10 +356,10 @@ namespace Flow.Launcher.Plugin.Program.Programs private static IEnumerable GetProgramsFromRegistry(RegistryKey root) { return root - .GetSubKeyNames() - .Select(x => GetProgramPathFromRegistrySubKeys(root, x)) - .Distinct() - .Select(x => GetProgramFromPath(x)); + .GetSubKeyNames() + .Select(x => GetProgramPathFromRegistrySubKeys(root, x)) + .Distinct() + .Select(GetProgramFromPath); } private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey) @@ -397,7 +385,7 @@ namespace Flow.Launcher.Plugin.Program.Programs catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { ProgramLogger.LogException($"|Win32|GetProgramPathFromRegistrySubKeys|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + $"|Permission denied when trying to load the program from {path}", e); return string.Empty; } @@ -419,13 +407,13 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } - private class Win32ComparatorBasedonDescription : IEqualityComparer + private class Win32ComparatorWithDescription : IEqualityComparer { - public readonly static Win32ComparatorBasedonDescription Default = new Win32ComparatorBasedonDescription(); + public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); public bool Equals(Win32 x, Win32 y) { - return x.Description == y.Description; + return x?.Description == y?.Description; } public int GetHashCode(Win32 obj) @@ -435,16 +423,16 @@ namespace Flow.Launcher.Plugin.Program.Programs } private static Win32[] ProgramsHasher(IEnumerable programs) - => programs.GroupBy(p => p.FullPath.ToLower()) - .SelectMany(g => - { - var tempList = g.ToList(); - if (tempList.Count() > 1) - return g.Where(p => !string.IsNullOrEmpty(p.Description)) - .Distinct(Win32ComparatorBasedonDescription.Default); - else - return g.Take(1); - }).ToArray(); + { + return programs.GroupBy(p => p.FullPath.ToLower()) + .SelectMany(g => + { + if (g.Count() > 1) + return g.Where(p => !string.IsNullOrEmpty(p.Description)) + .Distinct(Win32ComparatorWithDescription.Default); + return g; + }).ToArray(); + } public static Win32[] All(Settings settings) @@ -467,6 +455,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var startMenu = StartMenuPrograms(settings.ProgramSuffixes); programs = programs.Concat(startMenu); } + return ProgramsHasher(programs); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. @@ -486,4 +475,4 @@ namespace Flow.Launcher.Plugin.Program.Programs #endif } } -} +} \ No newline at end of file From 5d894ec154dca7949cb0d9cb8c67c22d9e667519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 25 Jan 2021 17:18:48 +0800 Subject: [PATCH 011/398] Don't await download manifest when it is unavaliable, but only await when calling install method to allow user using uninstall when manifest is not ready. --- .../Flow.Launcher.Plugin.PluginsManager/Main.cs | 15 +++++++++++++-- .../PluginsManager.cs | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 66bfd2ab5..7836f1441 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -69,8 +69,19 @@ namespace Flow.Launcher.Plugin.PluginsManager if ((DateTime.Now - lastUpdateTime).TotalHours > 12) // 12 hours { - await pluginManager.UpdateManifest(); - lastUpdateTime = DateTime.Now; + _ = pluginManager.UpdateManifest().ContinueWith(t => + { + if (t.IsCompletedSuccessfully) + { + lastUpdateTime = DateTime.Now; + } + else + { + Context.API.ShowMsg("Plugin Manifest Download Fail.", + "Please check if you can connect to github.com. " + + "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); + } + }); } return search switch diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 0f5e6d9e8..e25d97b36 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -46,9 +46,9 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = settings; } - internal async Task UpdateManifest() + internal Task UpdateManifest() { - await pluginsManifest.DownloadManifest(); + return _downloadManifestTask = pluginsManifest.DownloadManifest(); } internal List GetDefaultHotKeys() From 89453f441d6de762802ecb7e2eff729b2aae97b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 29 Jan 2021 11:37:26 +0800 Subject: [PATCH 012/398] Await Update when manifest is not ready --- .../Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- .../PluginsManager.cs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 7836f1441..c9f9ca2a7 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -88,7 +88,7 @@ namespace Flow.Launcher.Plugin.PluginsManager { var s when s.StartsWith(Settings.HotKeyInstall) => await pluginManager.RequestInstallOrUpdate(s, token), var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s), - var s when s.StartsWith(Settings.HotkeyUpdate) => pluginManager.RequestUpdate(s), + var s when s.StartsWith(Settings.HotkeyUpdate) => await pluginManager.RequestUpdate(s, token), _ => pluginManager.GetDefaultHotKeys().Where(hotkey => { hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index e25d97b36..eb0caec3f 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -149,8 +149,19 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.RestartApp(); } - internal List RequestUpdate(string search) + internal async ValueTask> RequestUpdate(string search, CancellationToken token) { + if (!pluginsManifest.UserPlugins.Any() && + _downloadManifestTask.Status != TaskStatus.Running) + { + _downloadManifestTask = pluginsManifest.DownloadManifest(); + } + + await _downloadManifestTask; + + token.ThrowIfCancellationRequested(); + + var autocompletedResults = AutoCompleteReturnAllResults(search, Settings.HotkeyUpdate, "Update", @@ -286,8 +297,7 @@ namespace Flow.Launcher.Plugin.PluginsManager await _downloadManifestTask; - if (token.IsCancellationRequested) - return null; + token.ThrowIfCancellationRequested(); var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); From 2271ec16fd556e88ccf21f1765e51ac27848ee18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 29 Jan 2021 12:17:17 +0800 Subject: [PATCH 013/398] Optimize exception message logic --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 13 ++----------- .../PluginsManager.cs | 6 +++++- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index c9f9ca2a7..dbd1600c9 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -71,17 +71,8 @@ namespace Flow.Launcher.Plugin.PluginsManager { _ = pluginManager.UpdateManifest().ContinueWith(t => { - if (t.IsCompletedSuccessfully) - { - lastUpdateTime = DateTime.Now; - } - else - { - Context.API.ShowMsg("Plugin Manifest Download Fail.", - "Please check if you can connect to github.com. " + - "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); - } - }); + lastUpdateTime = DateTime.Now; + }, TaskContinuationOptions.OnlyOnRanToCompletion); } return search switch diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index eb0caec3f..090e93bf2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -48,7 +48,11 @@ namespace Flow.Launcher.Plugin.PluginsManager internal Task UpdateManifest() { - return _downloadManifestTask = pluginsManifest.DownloadManifest(); + return _downloadManifestTask = pluginsManifest.DownloadManifest().ContinueWith(t => + Context.API.ShowMsg("Plugin Manifest Download Fail.", + "Please check if you can connect to github.com. " + + "This error means you may not be able to Install and Update Plugin.", icoPath, false), + TaskContinuationOptions.OnlyOnFaulted); } internal List GetDefaultHotKeys() From 4933a2e28c4d1572172e49ae0a23288ad9219bdd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 31 Jan 2021 17:49:08 +1100 Subject: [PATCH 014/398] add python installation --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 1 + Flow.Launcher.Core/Plugin/PluginsLoader.cs | 113 ++++++++++++++----- 2 files changed, 88 insertions(+), 26 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 189a6669e..deb73ba80 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -53,6 +53,7 @@ + diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index fcf178445..45be6c7c1 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Loader; using System.Threading.Tasks; using System.Windows.Forms; +using Droplex; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Core.Plugin { @@ -22,7 +23,7 @@ namespace Flow.Launcher.Core.Plugin public static List Plugins(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); - var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory); + var pythonPlugins = PythonPlugins(metadatas, settings); var executablePlugins = ExecutablePlugins(metadatas); var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList(); return plugins; @@ -113,11 +114,14 @@ namespace Flow.Launcher.Core.Plugin return plugins; } - public static IEnumerable PythonPlugins(List source, string pythonDirectory) + public static IEnumerable PythonPlugins(List source, PluginsSettings settings) { - // try to set Constant.PythonPath, either from + if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python)) + return new List(); + + // Try setting Constant.PythonPath first, either from // PATH or from the given pythonDirectory - if (string.IsNullOrEmpty(pythonDirectory)) + if (string.IsNullOrEmpty(settings.PythonDirectory)) { var paths = Environment.GetEnvironmentVariable(PATH); if (paths != null) @@ -129,47 +133,104 @@ namespace Flow.Launcher.Core.Plugin if (pythonInPath) { - Constant.PythonPath = PythonExecutable; + Constant.PythonPath = + Path.Combine(paths.Split(';').Where(p => p.ToLower().Contains(Python)).FirstOrDefault(), PythonExecutable); + settings.PythonDirectory = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, Constant.PythonPath); } else { - Log.Error("|PluginsLoader.PythonPlugins|Python can't be found in PATH."); + Log.Error("PluginsLoader","Failed to set Python path despite the environment variable PATH is found", "PythonPlugins"); } } - else - { - Log.Error("|PluginsLoader.PythonPlugins|PATH environment variable is not set."); - } } else { - var path = Path.Combine(pythonDirectory, PythonExecutable); + var path = Path.Combine(settings.PythonDirectory, PythonExecutable); if (File.Exists(path)) { Constant.PythonPath = path; } else { - Log.Error($"|PluginsLoader.PythonPlugins|Can't find python executable in {path}"); + Log.Error("PluginsLoader",$"Tried to automatically set from Settings.PythonDirectory " + + $"but can't find python executable in {path}", "PythonPlugins"); } } - // if we have a path to the python executable, - // load every python plugin pair. - if (String.IsNullOrEmpty(Constant.PythonPath)) + if (string.IsNullOrEmpty(settings.PythonDirectory)) { + if (MessageBox.Show("Flow detected you have installed Python plugins, " + + "would you like to install Python to run them? " + + Environment.NewLine + Environment.NewLine + + "Click no if it's already installed, " + + "and you will be prompted to select the folder that contains the Python executable", + string.Empty, MessageBoxButtons.YesNo) == DialogResult.No + && string.IsNullOrEmpty(settings.PythonDirectory)) + { + var dlg = new FolderBrowserDialog + { + SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + }; + + var result = dlg.ShowDialog(); + if (result == DialogResult.OK) + { + string pythonDirectory = dlg.SelectedPath; + if (!string.IsNullOrEmpty(pythonDirectory)) + { + var pythonPath = Path.Combine(pythonDirectory, PythonExecutable); + if (File.Exists(pythonPath)) + { + settings.PythonDirectory = pythonDirectory; + Constant.PythonPath = pythonPath; + } + else + { + MessageBox.Show("Can't find python in given directory"); + } + } + } + } + else + { + var installation = Task.Run(async () => { await DroplexPackage.Drop(App.python3_9_1).ConfigureAwait(false); }); + installation.Wait(); + + var installedPythonDirectory = + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39"); + var pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable); + if (FilesFolders.FileExists(pythonPath)) + { + settings.PythonDirectory = installedPythonDirectory; + Constant.PythonPath = pythonPath; + } + else + { + Log.Error("PluginsLoader", + $"Failed to set Python path after Droplex install, {pythonPath} does not exist", + "PythonPlugins"); + } + } + } + + if (string.IsNullOrEmpty(settings.PythonDirectory)) + { + MessageBox.Show("Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom)."); + Log.Error("PluginsLoader", + $"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.", + "PythonPlugins"); + return new List(); } - else - { - return source - .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) - .Select(metadata => new PluginPair - { - Plugin = new PythonPlugin(Constant.PythonPath), - Metadata = metadata - }); - } + + return source + .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) + .Select(metadata => new PluginPair + { + Plugin = new PythonPlugin(Constant.PythonPath), + Metadata = metadata + }) + .ToList(); } public static IEnumerable ExecutablePlugins(IEnumerable source) From a6e56025207ddf64087d93a6769cd025b8ab1d6e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 3 Feb 2021 13:00:16 +1100 Subject: [PATCH 015/398] formatting fix --- Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs | 2 +- Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index dbd1600c9..46a57757b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -48,7 +48,7 @@ namespace Flow.Launcher.Plugin.PluginsManager { context.API.ShowMsg("Plugin Manifest Download Fail.", "Please check if you can connect to github.com. " + - "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); + "This error means you may not be able to install or update plugins.", pluginManager.icoPath, false); } }); diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 090e93bf2..9606db7c1 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -165,7 +165,6 @@ namespace Flow.Launcher.Plugin.PluginsManager token.ThrowIfCancellationRequested(); - var autocompletedResults = AutoCompleteReturnAllResults(search, Settings.HotkeyUpdate, "Update", From 71664bddc0b42f3786031076f2de12f5afdaba5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 3 Feb 2021 10:23:38 +0800 Subject: [PATCH 016/398] Optimize Logic --- .../Main.cs | 18 ++++----------- .../PluginsManager.cs | 23 ++++++++++--------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index dbd1600c9..3707beee2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -37,20 +37,10 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = viewModel.Settings; contextMenu = new ContextMenu(Context); pluginManager = new PluginsManager(Context, Settings); - var updateManifestTask = pluginManager.UpdateManifest(); - _ = updateManifestTask.ContinueWith(t => - { - if (t.IsCompletedSuccessfully) - { - lastUpdateTime = DateTime.Now; - } - else - { - context.API.ShowMsg("Plugin Manifest Download Fail.", - "Please check if you can connect to github.com. " + - "This error means you may not be able to Install and Update Plugin.", pluginManager.icoPath, false); - } - }); + _ = pluginManager.UpdateManifest().ContinueWith(_ => + { + lastUpdateTime = DateTime.Now; + }, TaskContinuationOptions.OnlyOnRanToCompletion); return Task.CompletedTask; } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 090e93bf2..f14cd1be4 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -48,11 +48,18 @@ namespace Flow.Launcher.Plugin.PluginsManager internal Task UpdateManifest() { - return _downloadManifestTask = pluginsManifest.DownloadManifest().ContinueWith(t => + if (_downloadManifestTask.Status == TaskStatus.Running) + { + return _downloadManifestTask; + } + else + { + return _downloadManifestTask = pluginsManifest.DownloadManifest().ContinueWith(t => Context.API.ShowMsg("Plugin Manifest Download Fail.", "Please check if you can connect to github.com. " + "This error means you may not be able to Install and Update Plugin.", icoPath, false), TaskContinuationOptions.OnlyOnFaulted); + } } internal List GetDefaultHotKeys() @@ -155,14 +162,11 @@ namespace Flow.Launcher.Plugin.PluginsManager internal async ValueTask> RequestUpdate(string search, CancellationToken token) { - if (!pluginsManifest.UserPlugins.Any() && - _downloadManifestTask.Status != TaskStatus.Running) + if (!pluginsManifest.UserPlugins.Any()) { - _downloadManifestTask = pluginsManifest.DownloadManifest(); + await UpdateManifest(); } - await _downloadManifestTask; - token.ThrowIfCancellationRequested(); @@ -293,14 +297,11 @@ namespace Flow.Launcher.Plugin.PluginsManager internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token) { - if (!pluginsManifest.UserPlugins.Any() && - _downloadManifestTask.Status != TaskStatus.Running) + if (!pluginsManifest.UserPlugins.Any()) { - _downloadManifestTask = pluginsManifest.DownloadManifest(); + await UpdateManifest(); } - await _downloadManifestTask; - token.ThrowIfCancellationRequested(); var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); From 1f6e6e5e09590b071cd94757c745e0c88a889431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 3 Feb 2021 10:25:29 +0800 Subject: [PATCH 017/398] fix format --- .../Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index 5ea38b7c3..184e5c230 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -46,6 +46,9 @@ namespace Flow.Launcher.Plugin.PluginsManager Settings = settings; } + private Task _downloadManifestTask = Task.CompletedTask; + + internal Task UpdateManifest() { if (_downloadManifestTask.Status == TaskStatus.Running) @@ -292,8 +295,6 @@ namespace Flow.Launcher.Plugin.PluginsManager .ToList(); } - private Task _downloadManifestTask = Task.CompletedTask; - internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token) { if (!pluginsManifest.UserPlugins.Any()) From f3d0b8861641c70444cc85b26302336a37e12da1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 5 Feb 2021 08:08:33 +1100 Subject: [PATCH 018/398] remove task --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 45be6c7c1..2c75f9633 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -193,8 +193,7 @@ namespace Flow.Launcher.Core.Plugin } else { - var installation = Task.Run(async () => { await DroplexPackage.Drop(App.python3_9_1).ConfigureAwait(false); }); - installation.Wait(); + DroplexPackage.Drop(App.python3_9_1).Wait(); var installedPythonDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39"); From 1af95254874874029d818b7ffe3d3a0e2e26da47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 6 Feb 2021 16:12:38 +0800 Subject: [PATCH 019/398] move ISavable to Flow.Launcher.Plugin\Interfaces --- .../Storage => Flow.Launcher.Plugin/Interfaces}/ISavable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {Flow.Launcher.Infrastructure/Storage => Flow.Launcher.Plugin/Interfaces}/ISavable.cs (81%) diff --git a/Flow.Launcher.Infrastructure/Storage/ISavable.cs b/Flow.Launcher.Plugin/Interfaces/ISavable.cs similarity index 81% rename from Flow.Launcher.Infrastructure/Storage/ISavable.cs rename to Flow.Launcher.Plugin/Interfaces/ISavable.cs index 8294a3df8..7c1110e0e 100644 --- a/Flow.Launcher.Infrastructure/Storage/ISavable.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISavable.cs @@ -1,4 +1,4 @@ -namespace Flow.Launcher.Infrastructure.Storage +namespace Flow.Launcher.Plugin { /// /// Save plugin settings/cache, From 2b9823a7acfead7dbea79dbf8435ec187ec04720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 12:49:27 +0800 Subject: [PATCH 020/398] Use Customized Except for removing disabled source --- .../Programs/Win32.cs | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index b6ca7ffcc..48b0c4c32 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -285,9 +285,8 @@ namespace Flow.Launcher.Plugin.Program.Programs private static IEnumerable UnregisteredPrograms(List sources, string[] suffixes) { - var paths = sources.Where(s => Directory.Exists(s.Location) && s.Enabled) - .SelectMany(s => ProgramPaths(s.Location, suffixes)) - .Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier)) + var paths = ExceptDisabledSource(sources.Where(s => Directory.Exists(s.Location) && s.Enabled) + .SelectMany(s => ProgramPaths(s.Location, suffixes)), x => x) .Distinct(); var programs = paths.Select(x => Extension(x) switch @@ -312,9 +311,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var toFilter = paths1.Concat(paths2); - var programs = toFilter - .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) - .Distinct() + var programs = ExceptDisabledSource(toFilter.Distinct()) .Select(x => Extension(x) switch { ShortcutExtension => LnkProgram(x), @@ -327,39 +324,36 @@ namespace Flow.Launcher.Plugin.Program.Programs { // https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"; - IEnumerable programs = Enumerable.Empty(); + + IEnumerable toFilter = Enumerable.Empty(); using var rootMachine = Registry.LocalMachine.OpenSubKey(appPaths); using var rootUser = Registry.CurrentUser.OpenSubKey(appPaths); if (rootMachine != null) { - programs = programs.Concat(GetProgramsFromRegistry(rootMachine)); + toFilter = toFilter.Concat(GetPathFromRegistry(rootMachine)); } if (rootUser != null) { - programs = programs.Concat(GetProgramsFromRegistry(rootUser)); + toFilter = toFilter.Concat(GetPathFromRegistry(rootUser)); } - var disabledProgramsList = Main._settings.DisabledProgramSources; - var toFilter = programs.Where(p => suffixes.Contains(Extension(p.ExecutableName))); + toFilter = toFilter.Distinct().Where(p => suffixes.Contains(Extension(p))); - var filtered = toFilter - .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier)) - .Select(t1 => t1); + var filtered = ExceptDisabledSource(toFilter); - return filtered.ToList(); + return filtered.Select(GetProgramFromPath).ToList(); // ToList due to disposing issue } - private static IEnumerable GetProgramsFromRegistry(RegistryKey root) + private static IEnumerable GetPathFromRegistry(RegistryKey root) { return root .GetSubKeyNames() .Select(x => GetProgramPathFromRegistrySubKeys(root, x)) - .Distinct() - .Select(GetProgramFromPath); + .Distinct(); } private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey) @@ -394,12 +388,12 @@ namespace Flow.Launcher.Plugin.Program.Programs private static Win32 GetProgramFromPath(string path) { if (string.IsNullOrEmpty(path)) - return new Win32(); + return null; path = Environment.ExpandEnvironmentVariables(path); if (!File.Exists(path)) - return new Win32(); + return null; var entry = Win32Program(path); entry.ExecutableName = Path.GetFileName(path); @@ -407,19 +401,35 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } + public static IEnumerable ExceptDisabledSource(IEnumerable sources) => ExceptDisabledSource(sources, x => x); + + public static IEnumerable ExceptDisabledSource(IEnumerable sources, + Func keySelector) + { + return Main._settings.DisabledProgramSources.Count == 0 + ? sources + : ExceptDisabledSourceEnumerable(sources, keySelector); + + static IEnumerable ExceptDisabledSourceEnumerable(IEnumerable elements, + Func selector) + { + var set = Main._settings.DisabledProgramSources.Select(x => x.UniqueIdentifier).ToHashSet(); + + foreach (var element in elements) + { + if (!set.Contains(selector(element))) + yield return element; + } + } + } + private class Win32ComparatorWithDescription : IEqualityComparer { public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); - public bool Equals(Win32 x, Win32 y) - { - return x?.Description == y?.Description; - } + public bool Equals(Win32 x, Win32 y) => x?.Description == y?.Description; - public int GetHashCode(Win32 obj) - { - return obj.Description.GetHashCode(); - } + public int GetHashCode(Win32 obj) => obj.Description.GetHashCode(); } private static Win32[] ProgramsHasher(IEnumerable programs) @@ -456,7 +466,8 @@ namespace Flow.Launcher.Plugin.Program.Programs programs = programs.Concat(startMenu); } - return ProgramsHasher(programs); + + return ProgramsHasher(programs.Where(p => p != null)); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. catch (Exception e) From 70db600c9fb3d104dcf4eb7f2bb2c256a677cc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 12:56:40 +0800 Subject: [PATCH 021/398] optimize format --- .../Programs/Win32.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 48b0c4c32..c1de4b5da 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -401,7 +401,10 @@ namespace Flow.Launcher.Plugin.Program.Programs return entry; } - public static IEnumerable ExceptDisabledSource(IEnumerable sources) => ExceptDisabledSource(sources, x => x); + public static IEnumerable ExceptDisabledSource(IEnumerable sources) + { + return ExceptDisabledSource(sources, x => x); + } public static IEnumerable ExceptDisabledSource(IEnumerable sources, Func keySelector) @@ -427,9 +430,15 @@ namespace Flow.Launcher.Plugin.Program.Programs { public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); - public bool Equals(Win32 x, Win32 y) => x?.Description == y?.Description; + public bool Equals(Win32 x, Win32 y) + { + return x?.Description == y?.Description; + } - public int GetHashCode(Win32 obj) => obj.Description.GetHashCode(); + public int GetHashCode(Win32 obj) + { + return obj.Description.GetHashCode(); + } } private static Win32[] ProgramsHasher(IEnumerable programs) From 0da21b1ed75e05627622ea73269c8aad662a2392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 14:37:37 +0800 Subject: [PATCH 022/398] JSONRPC Async Model --- Flow.Launcher.Core/Plugin/ExecutablePlugin.cs | 20 +-- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 120 +++++++++--------- Flow.Launcher.Core/Plugin/PythonPlugin.cs | 12 +- 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs index 6a96a94b7..cbf048f16 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using Flow.Launcher.Plugin; namespace Flow.Launcher.Core.Plugin @@ -21,34 +23,36 @@ namespace Flow.Launcher.Core.Plugin }; } - protected override string ExecuteQuery(Query query) + protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { Method = "query", - Parameters = new object[] { query.Search }, + Parameters = new object[] {query.Search}, }; _startInfo.Arguments = $"\"{request}\""; - return Execute(_startInfo); + return ExecuteAsync(_startInfo, token); } protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest) { _startInfo.Arguments = $"\"{rpcRequest}\""; - return Execute(_startInfo); + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } - protected override string ExecuteContextMenu(Result selectedResult) { - JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { + protected override string ExecuteContextMenu(Result selectedResult) + { + JsonRPCServerRequestModel request = new JsonRPCServerRequestModel + { Method = "contextmenu", - Parameters = new object[] { selectedResult.ContextData }, + Parameters = new object[] {selectedResult.ContextData}, }; _startInfo.Arguments = $"\"{request}\""; - return Execute(_startInfo); + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index c7ad70391..626013cf0 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -1,15 +1,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Reflection; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using Flow.Launcher.Infrastructure.Exception; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin; +using JetBrains.Annotations; namespace Flow.Launcher.Core.Plugin { @@ -17,7 +16,7 @@ namespace Flow.Launcher.Core.Plugin /// Represent the plugin that using JsonPRC /// every JsonRPC plugin should has its own plugin instance /// - internal abstract class JsonRPCPlugin : IPlugin, IContextMenu + internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu { protected PluginInitContext context; public const string JsonRPC = "JsonRPC"; @@ -27,23 +26,10 @@ namespace Flow.Launcher.Core.Plugin /// public abstract string SupportedLanguage { get; set; } - protected abstract string ExecuteQuery(Query query); + protected abstract Task ExecuteQueryAsync(Query query, CancellationToken token); protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); protected abstract string ExecuteContextMenu(Result selectedResult); - public List Query(Query query) - { - string output = ExecuteQuery(query); - try - { - return DeserializedResult(output); - } - catch (Exception e) - { - Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e); - return null; - } - } public List LoadContextMenus(Result selectedResult) { @@ -65,44 +51,47 @@ namespace Flow.Launcher.Core.Plugin { List results = new List(); - JsonRPCQueryResponseModel queryResponseModel = JsonSerializer.Deserialize(output); + JsonRPCQueryResponseModel queryResponseModel = + JsonSerializer.Deserialize(output); if (queryResponseModel.Result == null) return null; foreach (JsonRPCResult result in queryResponseModel.Result) { - JsonRPCResult result1 = result; result.Action = c => { - if (result1.JsonRPCAction == null) return false; + if (result.JsonRPCAction == null) return false; - if (!String.IsNullOrEmpty(result1.JsonRPCAction.Method)) + if (!String.IsNullOrEmpty(result.JsonRPCAction.Method)) { - if (result1.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) + if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) { - ExecuteFlowLauncherAPI(result1.JsonRPCAction.Method.Substring(4), result1.JsonRPCAction.Parameters); + ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4), + result.JsonRPCAction.Parameters); } else { - string actionReponse = ExecuteCallback(result1.JsonRPCAction); - JsonRPCRequestModel jsonRpcRequestModel = JsonSerializer.Deserialize(actionReponse); + string actionReponse = ExecuteCallback(result.JsonRPCAction); + JsonRPCRequestModel jsonRpcRequestModel = + JsonSerializer.Deserialize(actionReponse); if (jsonRpcRequestModel != null && !String.IsNullOrEmpty(jsonRpcRequestModel.Method) && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) { - ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), jsonRpcRequestModel.Parameters); + ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), + jsonRpcRequestModel.Parameters); } } } - return !result1.JsonRPCAction.DontHideAfterAction; + + return !result.JsonRPCAction.DontHideAfterAction; }; results.Add(result); } + return results; } - else - { - return null; - } + + return null; } private void ExecuteFlowLauncherAPI(string method, object[] parameters) @@ -117,9 +106,7 @@ namespace Flow.Launcher.Core.Plugin catch (Exception) { #if (DEBUG) - { - throw; - } + throw; #endif } } @@ -130,8 +117,9 @@ namespace Flow.Launcher.Core.Plugin /// /// /// + /// Cancellation Token /// - protected string Execute(string fileName, string arguments) + protected async Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) { ProcessStartInfo start = new ProcessStartInfo(); start.FileName = fileName; @@ -140,10 +128,10 @@ namespace Flow.Launcher.Core.Plugin start.CreateNoWindow = true; start.RedirectStandardOutput = true; start.RedirectStandardError = true; - return Execute(start); + return await ExecuteAsync(start, token); } - protected string Execute(ProcessStartInfo startInfo) + protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) { try { @@ -155,45 +143,59 @@ namespace Flow.Launcher.Core.Plugin } using var standardOutput = process.StandardOutput; - var result = standardOutput.ReadToEnd(); + var result = await standardOutput.ReadToEndAsync(); + if (token.IsCancellationRequested) + return string.Empty; + if (string.IsNullOrEmpty(result)) { - using (var standardError = process.StandardError) + using var standardError = process.StandardError; + var error = await standardError.ReadToEndAsync(); + if (!string.IsNullOrEmpty(error)) { - var error = standardError.ReadToEnd(); - if (!string.IsNullOrEmpty(error)) - { - Log.Error($"|JsonRPCPlugin.Execute|{error}"); - return string.Empty; - } - else - { - Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error."); - return string.Empty; - } + Log.Error($"|JsonRPCPlugin.Execute|{error}"); + return string.Empty; } - } - else if (result.StartsWith("DEBUG:")) - { - MessageBox.Show(new Form { TopMost = true }, result.Substring(6)); + + Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error."); return string.Empty; } - else + + if (result.StartsWith("DEBUG:")) { - return result; + MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); + return string.Empty; } + return result; } catch (Exception e) { - Log.Exception($"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e); + Log.Exception( + $"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", + e); return string.Empty; } } - public void Init(PluginInitContext ctx) + public async Task> QueryAsync(Query query, CancellationToken token) { - context = ctx; + string output = await ExecuteQueryAsync(query, token); + try + { + return DeserializedResult(output); + } + catch (Exception e) + { + Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e); + return null; + } + } + + public Task InitAsync(PluginInitContext context) + { + this.context = context; + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 3c5a3a699..314726735 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics; using System.IO; +using System.Threading; +using System.Threading.Tasks; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin; @@ -28,7 +30,7 @@ namespace Flow.Launcher.Core.Plugin } - protected override string ExecuteQuery(Query query) + protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { @@ -40,14 +42,15 @@ namespace Flow.Launcher.Core.Plugin // todo happlebao why context can't be used in constructor _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; - return Execute(_startInfo); + return ExecuteAsync(_startInfo, token); } protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest) { _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{rpcRequest}\""; _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; - return Execute(_startInfo); + // TODO: Async Action + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } protected override string ExecuteContextMenu(Result selectedResult) { @@ -58,7 +61,8 @@ namespace Flow.Launcher.Core.Plugin _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{request}\""; _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; - return Execute(_startInfo); + // TODO: Async Action + return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); } } } \ No newline at end of file From b15319ea4da793d8401a97b0c782fd3164860097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Feb 2021 14:43:17 +0800 Subject: [PATCH 023/398] Keep the Sync version for ContextMenu and Callback --- Flow.Launcher.Core/Plugin/ExecutablePlugin.cs | 4 +- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 64 +++++++++++++++---- Flow.Launcher.Core/Plugin/PythonPlugin.cs | 4 +- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs index cbf048f16..d0d47aa58 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs @@ -39,7 +39,7 @@ namespace Flow.Launcher.Core.Plugin protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest) { _startInfo.Arguments = $"\"{rpcRequest}\""; - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } protected override string ExecuteContextMenu(Result selectedResult) @@ -52,7 +52,7 @@ namespace Flow.Launcher.Core.Plugin _startInfo.Arguments = $"\"{request}\""; - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } } } \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 626013cf0..7d221b74d 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -131,26 +131,20 @@ namespace Flow.Launcher.Core.Plugin return await ExecuteAsync(start, token); } - protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) + protected string Execute(ProcessStartInfo startInfo) { try { using var process = Process.Start(startInfo); - if (process == null) - { - Log.Error("|JsonRPCPlugin.Execute|Can't start new process"); - return string.Empty; - } - + if (process == null) return string.Empty; + using var standardOutput = process.StandardOutput; - var result = await standardOutput.ReadToEndAsync(); - if (token.IsCancellationRequested) - return string.Empty; + var result = standardOutput.ReadToEnd(); if (string.IsNullOrEmpty(result)) { using var standardError = process.StandardError; - var error = await standardError.ReadToEndAsync(); + var error = standardError.ReadToEnd(); if (!string.IsNullOrEmpty(error)) { Log.Error($"|JsonRPCPlugin.Execute|{error}"); @@ -168,6 +162,7 @@ namespace Flow.Launcher.Core.Plugin } return result; + } catch (Exception e) { @@ -178,6 +173,53 @@ namespace Flow.Launcher.Core.Plugin } } + protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) + { + try + { + using var process = Process.Start(startInfo); + if (process == null) + { + Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process"); + return string.Empty; + } + + using var standardOutput = process.StandardOutput; + var result = await standardOutput.ReadToEndAsync(); + if (token.IsCancellationRequested) + return string.Empty; + + if (string.IsNullOrEmpty(result)) + { + using var standardError = process.StandardError; + var error = await standardError.ReadToEndAsync(); + if (!string.IsNullOrEmpty(error)) + { + Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}"); + return string.Empty; + } + + Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error."); + return string.Empty; + } + + if (result.StartsWith("DEBUG:")) + { + MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); + return string.Empty; + } + + return result; + } + catch (Exception e) + { + Log.Exception( + $"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", + e); + return string.Empty; + } + } + public async Task> QueryAsync(Query query, CancellationToken token) { string output = await ExecuteQueryAsync(query, token); diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 314726735..750684954 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -50,7 +50,7 @@ namespace Flow.Launcher.Core.Plugin _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{rpcRequest}\""; _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; // TODO: Async Action - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } protected override string ExecuteContextMenu(Result selectedResult) { @@ -62,7 +62,7 @@ namespace Flow.Launcher.Core.Plugin _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; // TODO: Async Action - return ExecuteAsync(_startInfo).GetAwaiter().GetResult(); + return Execute(_startInfo); } } } \ No newline at end of file From f93dcaef73a9478d0c5920c9eca41ff2e91ff3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 9 Feb 2021 14:03:22 +0800 Subject: [PATCH 024/398] use throw instead of return empty string --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 7d221b74d..5b56f9347 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -185,14 +185,18 @@ namespace Flow.Launcher.Core.Plugin } using var standardOutput = process.StandardOutput; + + token.ThrowIfCancellationRequested(); + var result = await standardOutput.ReadToEndAsync(); - if (token.IsCancellationRequested) - return string.Empty; + token.ThrowIfCancellationRequested(); if (string.IsNullOrEmpty(result)) { using var standardError = process.StandardError; var error = await standardError.ReadToEndAsync(); + token.ThrowIfCancellationRequested(); + if (!string.IsNullOrEmpty(error)) { Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}"); From 276395e5f47165950aa609315afb33a251e76062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 9 Feb 2021 15:59:55 +0800 Subject: [PATCH 025/398] Use customized distinctBy instead of customized equalitycomparator --- .../Programs/Win32.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f2adbf243..d73483818 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -446,18 +446,13 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - private class Win32ComparatorWithDescription : IEqualityComparer + public static IEnumerable DistinctBy(IEnumerable source, Func selector) { - public static readonly Win32ComparatorWithDescription Default = new Win32ComparatorWithDescription(); - - public bool Equals(Win32 x, Win32 y) + var set = new HashSet(); + foreach (var item in source) { - return x?.Description == y?.Description; - } - - public int GetHashCode(Win32 obj) - { - return obj.Description.GetHashCode(); + if (set.Add(selector(item))) + yield return item; } } @@ -467,8 +462,7 @@ namespace Flow.Launcher.Plugin.Program.Programs .SelectMany(g => { if (g.Count() > 1) - return g.Where(p => !string.IsNullOrEmpty(p.Description)) - .Distinct(Win32ComparatorWithDescription.Default); + return DistinctBy(g.Where(p => !string.IsNullOrEmpty(p.Description)), x => x.Description); return g; }).ToArray(); } From 20f895c7810a3559800b3f5b7a3f9cf5e59215de Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 10 Feb 2021 21:17:03 +1100 Subject: [PATCH 026/398] upgrade Droplex to use mirror site --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index deb73ba80..b0cc07e1a 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -53,7 +53,7 @@ - + From 89ac8e5ee0a27adcc741c87078bb1c7930645c1d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 11 Feb 2021 08:47:30 +1100 Subject: [PATCH 027/398] add port plugins documentation --- .../Default Icons/app_missing_img.png | Bin .../Default Icons/app_missing_img_01.png | Bin .../Default Icons/app_missing_img_01.svg | 0 .../Default Icons/app_missing_img_02.png | Bin .../Default Icons/app_missing_img_02.svg | 0 .../Default Icons/app_missing_img_03.png | Bin .../Default Icons/app_missing_img_03.svg | 0 .../Default Icons/app_missing_img_buttons.png | Bin .../Default Icons/app_missing_img_buttons.svg | 0 .../Default Icons/app_missing_img_fluent.png | Bin .../Default Icons/app_missing_img_fluent.svg | 0 .../Default Icons/app_missing_img_huge.png | Bin .../Default Icons/app_missing_img_huge.svg | 0 .../app_missing_img_lightblue.png | Bin .../app_missing_img_lightblue.svg | 0 .../app_missing_img_lightblue_buttons.png | Bin .../app_missing_img_lightblue_buttons.svg | 0 .../Default Icons/app_missing_img_minimal.png | Bin .../Default Icons/app_missing_img_minimal.svg | 0 .../app_missing_img_minimal_buttons.png | Bin .../app_missing_img_minimal_buttons.svg | 0 {Doc => Artworks}/Logo/app_error.png | Bin {Doc => Artworks}/Logo/logo.ico | Bin {Doc => Artworks}/Logo/logo.png | Bin {Doc => Artworks}/Logo/logo.svg | 0 {Doc => Artworks}/Logo/logo128.png | Bin {Doc => Artworks}/Logo/logo16.png | Bin {Doc => Artworks}/Logo/logo256.png | Bin {Doc => Artworks}/Logo/logo32.png | Bin {Doc => Artworks}/Logo/logo48.png | Bin {Doc => Artworks}/Logo/logo512.png | Bin {Doc => Artworks}/Logo/logo64.png | Bin .../flow-header-landscape-transparent.png | Bin .../Logo/resources/flow-header-landscape.png | Bin .../flow-header-square-transparent.png | Bin .../Logo/resources/flow-header-square.png | Bin {Doc => Artworks}/Logo/resources/flow-logo.ai | 0 {Doc => Artworks}/Logo/resources/preview.pdf | 0 {Doc => Artworks}/app.ico | Bin {Doc => Artworks}/app.png | Bin {Doc => Artworks}/app_error.png | Bin {Doc => Artworks}/app_missing_img.png | Bin {Doc => Artworks}/mainsearch.png | Bin Docs/port-plugins.md | 17 +++++++++++++++++ 44 files changed, 17 insertions(+) rename {Doc => Artworks}/Default Icons/app_missing_img.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_01.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_01.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_02.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_02.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_03.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_03.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_buttons.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_buttons.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_fluent.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_fluent.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_huge.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_huge.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_lightblue.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_lightblue.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_lightblue_buttons.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_lightblue_buttons.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_minimal.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_minimal.svg (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_minimal_buttons.png (100%) rename {Doc => Artworks}/Default Icons/app_missing_img_minimal_buttons.svg (100%) rename {Doc => Artworks}/Logo/app_error.png (100%) rename {Doc => Artworks}/Logo/logo.ico (100%) rename {Doc => Artworks}/Logo/logo.png (100%) rename {Doc => Artworks}/Logo/logo.svg (100%) rename {Doc => Artworks}/Logo/logo128.png (100%) rename {Doc => Artworks}/Logo/logo16.png (100%) rename {Doc => Artworks}/Logo/logo256.png (100%) rename {Doc => Artworks}/Logo/logo32.png (100%) rename {Doc => Artworks}/Logo/logo48.png (100%) rename {Doc => Artworks}/Logo/logo512.png (100%) rename {Doc => Artworks}/Logo/logo64.png (100%) rename {Doc => Artworks}/Logo/resources/flow-header-landscape-transparent.png (100%) rename {Doc => Artworks}/Logo/resources/flow-header-landscape.png (100%) rename {Doc => Artworks}/Logo/resources/flow-header-square-transparent.png (100%) rename {Doc => Artworks}/Logo/resources/flow-header-square.png (100%) rename {Doc => Artworks}/Logo/resources/flow-logo.ai (100%) rename {Doc => Artworks}/Logo/resources/preview.pdf (100%) rename {Doc => Artworks}/app.ico (100%) rename {Doc => Artworks}/app.png (100%) rename {Doc => Artworks}/app_error.png (100%) rename {Doc => Artworks}/app_missing_img.png (100%) rename {Doc => Artworks}/mainsearch.png (100%) create mode 100644 Docs/port-plugins.md diff --git a/Doc/Default Icons/app_missing_img.png b/Artworks/Default Icons/app_missing_img.png similarity index 100% rename from Doc/Default Icons/app_missing_img.png rename to Artworks/Default Icons/app_missing_img.png diff --git a/Doc/Default Icons/app_missing_img_01.png b/Artworks/Default Icons/app_missing_img_01.png similarity index 100% rename from Doc/Default Icons/app_missing_img_01.png rename to Artworks/Default Icons/app_missing_img_01.png diff --git a/Doc/Default Icons/app_missing_img_01.svg b/Artworks/Default Icons/app_missing_img_01.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_01.svg rename to Artworks/Default Icons/app_missing_img_01.svg diff --git a/Doc/Default Icons/app_missing_img_02.png b/Artworks/Default Icons/app_missing_img_02.png similarity index 100% rename from Doc/Default Icons/app_missing_img_02.png rename to Artworks/Default Icons/app_missing_img_02.png diff --git a/Doc/Default Icons/app_missing_img_02.svg b/Artworks/Default Icons/app_missing_img_02.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_02.svg rename to Artworks/Default Icons/app_missing_img_02.svg diff --git a/Doc/Default Icons/app_missing_img_03.png b/Artworks/Default Icons/app_missing_img_03.png similarity index 100% rename from Doc/Default Icons/app_missing_img_03.png rename to Artworks/Default Icons/app_missing_img_03.png diff --git a/Doc/Default Icons/app_missing_img_03.svg b/Artworks/Default Icons/app_missing_img_03.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_03.svg rename to Artworks/Default Icons/app_missing_img_03.svg diff --git a/Doc/Default Icons/app_missing_img_buttons.png b/Artworks/Default Icons/app_missing_img_buttons.png similarity index 100% rename from Doc/Default Icons/app_missing_img_buttons.png rename to Artworks/Default Icons/app_missing_img_buttons.png diff --git a/Doc/Default Icons/app_missing_img_buttons.svg b/Artworks/Default Icons/app_missing_img_buttons.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_buttons.svg rename to Artworks/Default Icons/app_missing_img_buttons.svg diff --git a/Doc/Default Icons/app_missing_img_fluent.png b/Artworks/Default Icons/app_missing_img_fluent.png similarity index 100% rename from Doc/Default Icons/app_missing_img_fluent.png rename to Artworks/Default Icons/app_missing_img_fluent.png diff --git a/Doc/Default Icons/app_missing_img_fluent.svg b/Artworks/Default Icons/app_missing_img_fluent.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_fluent.svg rename to Artworks/Default Icons/app_missing_img_fluent.svg diff --git a/Doc/Default Icons/app_missing_img_huge.png b/Artworks/Default Icons/app_missing_img_huge.png similarity index 100% rename from Doc/Default Icons/app_missing_img_huge.png rename to Artworks/Default Icons/app_missing_img_huge.png diff --git a/Doc/Default Icons/app_missing_img_huge.svg b/Artworks/Default Icons/app_missing_img_huge.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_huge.svg rename to Artworks/Default Icons/app_missing_img_huge.svg diff --git a/Doc/Default Icons/app_missing_img_lightblue.png b/Artworks/Default Icons/app_missing_img_lightblue.png similarity index 100% rename from Doc/Default Icons/app_missing_img_lightblue.png rename to Artworks/Default Icons/app_missing_img_lightblue.png diff --git a/Doc/Default Icons/app_missing_img_lightblue.svg b/Artworks/Default Icons/app_missing_img_lightblue.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_lightblue.svg rename to Artworks/Default Icons/app_missing_img_lightblue.svg diff --git a/Doc/Default Icons/app_missing_img_lightblue_buttons.png b/Artworks/Default Icons/app_missing_img_lightblue_buttons.png similarity index 100% rename from Doc/Default Icons/app_missing_img_lightblue_buttons.png rename to Artworks/Default Icons/app_missing_img_lightblue_buttons.png diff --git a/Doc/Default Icons/app_missing_img_lightblue_buttons.svg b/Artworks/Default Icons/app_missing_img_lightblue_buttons.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_lightblue_buttons.svg rename to Artworks/Default Icons/app_missing_img_lightblue_buttons.svg diff --git a/Doc/Default Icons/app_missing_img_minimal.png b/Artworks/Default Icons/app_missing_img_minimal.png similarity index 100% rename from Doc/Default Icons/app_missing_img_minimal.png rename to Artworks/Default Icons/app_missing_img_minimal.png diff --git a/Doc/Default Icons/app_missing_img_minimal.svg b/Artworks/Default Icons/app_missing_img_minimal.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_minimal.svg rename to Artworks/Default Icons/app_missing_img_minimal.svg diff --git a/Doc/Default Icons/app_missing_img_minimal_buttons.png b/Artworks/Default Icons/app_missing_img_minimal_buttons.png similarity index 100% rename from Doc/Default Icons/app_missing_img_minimal_buttons.png rename to Artworks/Default Icons/app_missing_img_minimal_buttons.png diff --git a/Doc/Default Icons/app_missing_img_minimal_buttons.svg b/Artworks/Default Icons/app_missing_img_minimal_buttons.svg similarity index 100% rename from Doc/Default Icons/app_missing_img_minimal_buttons.svg rename to Artworks/Default Icons/app_missing_img_minimal_buttons.svg diff --git a/Doc/Logo/app_error.png b/Artworks/Logo/app_error.png similarity index 100% rename from Doc/Logo/app_error.png rename to Artworks/Logo/app_error.png diff --git a/Doc/Logo/logo.ico b/Artworks/Logo/logo.ico similarity index 100% rename from Doc/Logo/logo.ico rename to Artworks/Logo/logo.ico diff --git a/Doc/Logo/logo.png b/Artworks/Logo/logo.png similarity index 100% rename from Doc/Logo/logo.png rename to Artworks/Logo/logo.png diff --git a/Doc/Logo/logo.svg b/Artworks/Logo/logo.svg similarity index 100% rename from Doc/Logo/logo.svg rename to Artworks/Logo/logo.svg diff --git a/Doc/Logo/logo128.png b/Artworks/Logo/logo128.png similarity index 100% rename from Doc/Logo/logo128.png rename to Artworks/Logo/logo128.png diff --git a/Doc/Logo/logo16.png b/Artworks/Logo/logo16.png similarity index 100% rename from Doc/Logo/logo16.png rename to Artworks/Logo/logo16.png diff --git a/Doc/Logo/logo256.png b/Artworks/Logo/logo256.png similarity index 100% rename from Doc/Logo/logo256.png rename to Artworks/Logo/logo256.png diff --git a/Doc/Logo/logo32.png b/Artworks/Logo/logo32.png similarity index 100% rename from Doc/Logo/logo32.png rename to Artworks/Logo/logo32.png diff --git a/Doc/Logo/logo48.png b/Artworks/Logo/logo48.png similarity index 100% rename from Doc/Logo/logo48.png rename to Artworks/Logo/logo48.png diff --git a/Doc/Logo/logo512.png b/Artworks/Logo/logo512.png similarity index 100% rename from Doc/Logo/logo512.png rename to Artworks/Logo/logo512.png diff --git a/Doc/Logo/logo64.png b/Artworks/Logo/logo64.png similarity index 100% rename from Doc/Logo/logo64.png rename to Artworks/Logo/logo64.png diff --git a/Doc/Logo/resources/flow-header-landscape-transparent.png b/Artworks/Logo/resources/flow-header-landscape-transparent.png similarity index 100% rename from Doc/Logo/resources/flow-header-landscape-transparent.png rename to Artworks/Logo/resources/flow-header-landscape-transparent.png diff --git a/Doc/Logo/resources/flow-header-landscape.png b/Artworks/Logo/resources/flow-header-landscape.png similarity index 100% rename from Doc/Logo/resources/flow-header-landscape.png rename to Artworks/Logo/resources/flow-header-landscape.png diff --git a/Doc/Logo/resources/flow-header-square-transparent.png b/Artworks/Logo/resources/flow-header-square-transparent.png similarity index 100% rename from Doc/Logo/resources/flow-header-square-transparent.png rename to Artworks/Logo/resources/flow-header-square-transparent.png diff --git a/Doc/Logo/resources/flow-header-square.png b/Artworks/Logo/resources/flow-header-square.png similarity index 100% rename from Doc/Logo/resources/flow-header-square.png rename to Artworks/Logo/resources/flow-header-square.png diff --git a/Doc/Logo/resources/flow-logo.ai b/Artworks/Logo/resources/flow-logo.ai similarity index 100% rename from Doc/Logo/resources/flow-logo.ai rename to Artworks/Logo/resources/flow-logo.ai diff --git a/Doc/Logo/resources/preview.pdf b/Artworks/Logo/resources/preview.pdf similarity index 100% rename from Doc/Logo/resources/preview.pdf rename to Artworks/Logo/resources/preview.pdf diff --git a/Doc/app.ico b/Artworks/app.ico similarity index 100% rename from Doc/app.ico rename to Artworks/app.ico diff --git a/Doc/app.png b/Artworks/app.png similarity index 100% rename from Doc/app.png rename to Artworks/app.png diff --git a/Doc/app_error.png b/Artworks/app_error.png similarity index 100% rename from Doc/app_error.png rename to Artworks/app_error.png diff --git a/Doc/app_missing_img.png b/Artworks/app_missing_img.png similarity index 100% rename from Doc/app_missing_img.png rename to Artworks/app_missing_img.png diff --git a/Doc/mainsearch.png b/Artworks/mainsearch.png similarity index 100% rename from Doc/mainsearch.png rename to Artworks/mainsearch.png diff --git a/Docs/port-plugins.md b/Docs/port-plugins.md new file mode 100644 index 000000000..d12830124 --- /dev/null +++ b/Docs/port-plugins.md @@ -0,0 +1,17 @@ +# Porting Wox Plugins + +Note: +- When porting, please keep the author's commit history +- Flow Launcher targets .Net Core 3.1, so plugins should also be upgraded to keep the continuity of future developments + +Steps: +1. to start off, you can fork/create a new repo, either way the project's commit history must be kept. If it's forked, you can just start updating it. If it's a new repo, do this by first cloning the repo, then add your new repo as a new repo remote, remove the original remote and then push to it. +2. use try convert tool from https://github.com/dotnet/try-convert +3. try-convert -w path-to-folder-or-solution-or-project +4. May need to fix on the project file, a good template to follow is the [Explorer plugin](https://github.com/Flow-Launcher/Flow.Launcher/blob/dev/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj) project: + - fix to netcoreapp3.1 + - set the output location as 'Output\Release\' + - add true and false + - bump version to 2.0.0 and fix up any missing attributes if neccessary +5. update code and fix plugin's setting layout if neccessary +6. update readme to indicate where this port is from and the original author of the project \ No newline at end of file From 416ad3bb89bc862eae865da0090714d98e8a4f82 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 11 Feb 2021 14:00:17 +1100 Subject: [PATCH 028/398] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9af47a5ee..39e8a7dea 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,11 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up. **Integrations** - - If you use Python plugins: - - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. + - Python plugins: + - Once a Python plugin has been detected at start up, you will be prompted to either select the location or allow Python 3.9.1 to automatic download and install. - Add Python to `%PATH%` or set it in flow's settings. - - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. + - Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`. + - The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`. - Start to launch your Python plugins. - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). From a37f1918ae33a12e5a9daf0736db7c550d579002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 12 Feb 2021 03:17:13 +0800 Subject: [PATCH 029/398] Update all profile to dotnet 5 --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 4 ++-- .../Flow.Launcher.Infrastructure.csproj | 4 ++-- .../Flow.Launcher.Plugin.csproj | 4 ++-- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 2 +- Flow.Launcher/Flow.Launcher.csproj | 6 +++--- ...ined.pubxml => Net5.0-SelfContained.pubxml} | 2 +- ...Flow.Launcher.Plugin.BrowserBookmark.csproj | 17 +++-------------- .../Flow.Launcher.Plugin.Calculator.csproj | 4 ++-- .../Flow.Launcher.Plugin.ControlPanel.csproj | 4 ++-- .../Flow.Launcher.Plugin.Explorer.csproj | 4 ++-- ...Flow.Launcher.Plugin.PluginIndicator.csproj | 4 ++-- .../Flow.Launcher.Plugin.PluginsManager.csproj | 4 ++-- .../Flow.Launcher.Plugin.ProcessKiller.csproj | 2 +- .../Flow.Launcher.Plugin.Program.csproj | 5 ++--- .../Flow.Launcher.Plugin.Shell.csproj | 4 ++-- .../Flow.Launcher.Plugin.Sys.csproj | 4 ++-- .../Flow.Launcher.Plugin.Url.csproj | 4 ++-- .../Flow.Launcher.Plugin.WebSearch.csproj | 18 +++--------------- Scripts/flowlauncher.nuspec | 2 +- Scripts/post_build.ps1 | 2 +- global.json | 2 +- 21 files changed, 39 insertions(+), 63 deletions(-) rename Flow.Launcher/Properties/PublishProfiles/{NetCore3.1-SelfContained.pubxml => Net5.0-SelfContained.pubxml} (90%) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 189a6669e..8eb411707 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + net5.0-windows true true Library diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 8153de6c8..eda00b435 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + net5.0-windows {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3} Library true diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 0eefe5c4f..f2ee5f1af 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.1 + net5.0-windows {8451ECDD-2EA4-4966-BB0A-7BBC40138E80} true Library diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index e970c47b9..c9f495bfe 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0-windows10.0.19041.0 {FF742965-9A80-41A5-B042-D6C7D3A21708} Library Properties diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 289a502d0..f8459830c 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -1,8 +1,8 @@ - + WinExe - netcoreapp3.1 + net5.0-windows10.0.19041.0 true true Flow.Launcher.App @@ -78,7 +78,7 @@ - + all diff --git a/Flow.Launcher/Properties/PublishProfiles/NetCore3.1-SelfContained.pubxml b/Flow.Launcher/Properties/PublishProfiles/Net5.0-SelfContained.pubxml similarity index 90% rename from Flow.Launcher/Properties/PublishProfiles/NetCore3.1-SelfContained.pubxml rename to Flow.Launcher/Properties/PublishProfiles/Net5.0-SelfContained.pubxml index 2794a0cea..124792e3e 100644 --- a/Flow.Launcher/Properties/PublishProfiles/NetCore3.1-SelfContained.pubxml +++ b/Flow.Launcher/Properties/PublishProfiles/Net5.0-SelfContained.pubxml @@ -7,7 +7,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release Any CPU - netcoreapp3.1 + net5.0-windows10.0.19041.0 ..\Output\Release\ win-x64 true diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index d2a8736a6..d67aae1c9 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -1,8 +1,9 @@ - + Library - netcoreapp3.1 + net5.0-windows + true {9B130CC5-14FB-41FF-B310-0A95B6894C37} Properties Flow.Launcher.Plugin.BrowserBookmark @@ -54,22 +55,10 @@ - - MSBuild:Compile - Designer - PreserveNewest - PreserveNewest - - - - MSBuild:Compile - Designer - - diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index 1090926fc..b3212794b 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows {59BD9891-3837-438A-958D-ADC7F91F6F7E} Properties Flow.Launcher.Plugin.Caculator diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj index 06969a135..d69547c6c 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows {1EE20B48-82FB-48A2-8086-675D6DDAB4F0} Properties Flow.Launcher.Plugin.ControlPanel diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 9f0b46d93..71c0463b5 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows true true true diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index cc280b9a9..54f0bbd9b 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows {FDED22C8-B637-42E8-824A-63B5B6E05A3A} Properties Flow.Launcher.Plugin.PluginIndicator diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj index cb2507a2b..fe720d6dd 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -1,7 +1,7 @@ - + Library - netcoreapp3.1 + net5.0-windows true true true diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index a643ebf86..f7a33a94b 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -2,7 +2,7 @@ Library - netcoreapp3.1 + net5.0-windows Flow.Launcher.Plugin.ProcessKiller Flow.Launcher.Plugin.ProcessKiller Flow-Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 12e113855..986ce218c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows10.0.19041.0 {FDB3555B-58EF-4AE6-B5F1-904719637AB4} Properties Flow.Launcher.Plugin.Program @@ -69,7 +69,6 @@ - diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index d3042722b..1caf2499d 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} Properties Flow.Launcher.Plugin.Shell diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index c25e759d3..f4e3d1f36 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows {0B9DE348-9361-4940-ADB6-F5953BFFCCEC} Properties Flow.Launcher.Plugin.Sys diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index 671a8b1c2..4fb0b1205 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -1,8 +1,8 @@ - + Library - netcoreapp3.1 + net5.0-windows {A3DCCBCA-ACC1-421D-B16E-210896234C26} true Properties diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index af86af842..9fc1552ce 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -1,10 +1,11 @@ - + Library - netcoreapp3.1 + net5.0-windows {403B57F2-1856-4FC7-8A24-36AB346B763E} Properties + true Flow.Launcher.Plugin.WebSearch Flow.Launcher.Plugin.WebSearch true @@ -35,22 +36,9 @@ - - MSBuild:Compile - Designer - PreserveNewest - PreserveNewest - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - diff --git a/Scripts/flowlauncher.nuspec b/Scripts/flowlauncher.nuspec index 6c5deb86b..8877bfbaf 100644 --- a/Scripts/flowlauncher.nuspec +++ b/Scripts/flowlauncher.nuspec @@ -11,6 +11,6 @@ Flow Launcher - a launcher for windows - + diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 58d5c6a4e..ce9944624 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -90,7 +90,7 @@ function Pack-Squirrel-Installer ($path, $version, $output) { function Publish-Self-Contained ($p) { $csproj = Join-Path "$p" "Flow.Launcher/Flow.Launcher.csproj" -Resolve - $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/Net5-SelfContained.pubxml" -Resolve + $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/Net5.0-SelfContained.pubxml" -Resolve # we call dotnet publish on the main project. # The other projects should have been built in Release at this point. diff --git a/global.json b/global.json index c3efffd40..322cf958d 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "3.1.201", + "version": "5.0.103", "rollForward": "latestFeature" } } \ No newline at end of file From b4bc4f0d24922fe81a51df00c38c351cb0aadfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 12 Feb 2021 03:18:38 +0800 Subject: [PATCH 030/398] address a weird bug --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 5db26aa70..20d8f185b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -193,7 +193,15 @@ namespace Flow.Launcher.Plugin.Program.Programs if (u != null) { var id = u.Value; - var m = new PackageManager(); + PackageManager m; + try + { + m = new PackageManager(); + } + catch + { + m = new PackageManager(); + } var ps = m.FindPackagesForUser(id); ps = ps.Where(p => { From 73aa7a1636ec6230dd81e4890645bad726933650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 12 Feb 2021 03:41:51 +0800 Subject: [PATCH 031/398] change version number for global.json --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 322cf958d..2ad4d9100 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "5.0.103", + "version": "5.0.100", "rollForward": "latestFeature" } } \ No newline at end of file From 80f1585f88e1f17492d5c9896e8f91c32e610978 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 12 Feb 2021 14:45:31 +1100 Subject: [PATCH 032/398] add press F5 to reload all plugin data --- Flow.Launcher/Languages/en.xaml | 1 + Flow.Launcher/MainWindow.xaml | 1 + Flow.Launcher/Msg.xaml.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 29 +++++++++++++++++++----- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index b6bf76b7f..a036949fc 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -121,6 +121,7 @@ New Action Keyword can't be empty This new Action Keyword is already assigned to another plugin, please choose a different one Success + Completed successfully Use * if you don't want to specify an action keyword diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 4cc0b4428..aa0240dd4 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -34,6 +34,7 @@ + diff --git a/Flow.Launcher/Msg.xaml.cs b/Flow.Launcher/Msg.xaml.cs index 4129ce28b..6bb2fc2dc 100644 --- a/Flow.Launcher/Msg.xaml.cs +++ b/Flow.Launcher/Msg.xaml.cs @@ -66,7 +66,7 @@ namespace Flow.Launcher } if (!File.Exists(iconPath)) { - imgIco.Source = ImageLoader.Load(Path.Combine(Infrastructure.Constant.ProgramDirectory, "Images\\app.png")); + imgIco.Source = ImageLoader.Load(Path.Combine(Constant.ProgramDirectory, "Images\\app.png")); } else { imgIco.Source = ImageLoader.Load(iconPath); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index afbe6e197..a12912b67 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -89,7 +89,6 @@ namespace Flow.Launcher.ViewModel _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); - async Task updateAction() { var queue = new Dictionary(); @@ -105,9 +104,7 @@ namespace Flow.Launcher.ViewModel UpdateResultView(queue.Values); } - } - - ; + }; void continueAction(Task t) { @@ -115,8 +112,8 @@ namespace Flow.Launcher.ViewModel throw t.Exception; #else Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}"); - _resultsViewUpdateTask = - Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); + _resultsViewUpdateTask = + Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); #endif } } @@ -225,6 +222,25 @@ namespace Flow.Launcher.ViewModel SelectedResults = Results; } }); + + ReloadPluginDataCommand = new RelayCommand(_ => + { + var msg = new Msg { Owner = Application.Current.MainWindow }; + + MainWindowVisibility = Visibility.Collapsed; + + PluginManager + .ReloadData() + .ContinueWith(_ => + Application.Current.Dispatcher.Invoke(() => + { + msg.Show( + InternationalizationManager.Instance.GetTranslation("success"), + InternationalizationManager.Instance.GetTranslation("completedSuccessfully"), + ""); + })) + .ConfigureAwait(false); + }); } #endregion @@ -313,6 +329,7 @@ namespace Flow.Launcher.ViewModel public ICommand LoadContextMenuCommand { get; set; } public ICommand LoadHistoryCommand { get; set; } public ICommand OpenResultCommand { get; set; } + public ICommand ReloadPluginDataCommand { get; set; } public string OpenResultCommandModifiers { get; private set; } From fcc0bb11facfa21e795b2f7fac10a93bbf6c4949 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 12 Feb 2021 14:49:21 +1100 Subject: [PATCH 033/398] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9af47a5ee..3e420bfac 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Windows may complain about security due to code not being signed, this will be c - Open context menu: on the selected result, press Ctrl+O/Shift+Enter. - Cancel/Return to previous screen: Esc. - Install/Uninstall/Update plugins: in the search window, type `pm install`/`pm uninstall`/`pm update` + the plugin name. +- Press `F5` while in the query window to reload all plugin data. - Saved user settings are located: - If using roaming: `%APPDATA%\FlowLauncher` - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` From aabb820f6f7e017bedff097ab6a03ba03433721f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 12:02:59 +0800 Subject: [PATCH 034/398] Optimize ToString() method override --- Flow.Launcher.Core/Plugin/JsonPRCModel.cs | 33 ++++++++-------------- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 4 +-- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs index 247fe1889..a0731f8fb 100644 --- a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs +++ b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs @@ -58,13 +58,12 @@ namespace Flow.Launcher.Core.Plugin string rpc = string.Empty; if (Parameters != null && Parameters.Length > 0) { - string parameters = Parameters.Aggregate("[", (current, o) => current + (GetParameterByType(o) + ",")); - parameters = parameters.Substring(0, parameters.Length - 1) + "]"; - rpc = string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":{1}", Method, parameters); + string parameters = $"[{string.Join(',', Parameters.Select(GetParameterByType))}]"; + rpc = $@"{{\""method\"":\""{Method}\"",\""parameters\"":{parameters}"; } else { - rpc = string.Format(@"{{\""method\"":\""{0}\"",\""parameters\"":[]", Method); + rpc = $@"{{\""method\"":\""{Method}\"",\""parameters\"":[]"; } return rpc; @@ -72,26 +71,16 @@ namespace Flow.Launcher.Core.Plugin } private string GetParameterByType(object parameter) + => parameter switch { - if (parameter == null) { - return "null"; - } - if (parameter is string) - { - return string.Format(@"\""{0}\""", ReplaceEscapes(parameter.ToString())); - } - if (parameter is int || parameter is float || parameter is double) - { - return string.Format(@"{0}", parameter); - } - if (parameter is bool) - { - return string.Format(@"{0}", parameter.ToString().ToLower()); - } - return parameter.ToString(); - } + null => "null", + string _ => $@"\""{ReplaceEscapes(parameter.ToString())}\""", + bool _ => $@"{parameter.ToString().ToLower()}", + _ => parameter.ToString() + }; - private string ReplaceEscapes(string str) + + private string ReplaceEscapes(string str) { return str.Replace(@"\", @"\\") //Escapes in ProcessStartInfo .Replace(@"\", @"\\") //Escapes itself when passed to client diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 5b56f9347..a37b2b68b 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -119,7 +119,7 @@ namespace Flow.Launcher.Core.Plugin /// /// Cancellation Token /// - protected async Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) + protected Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) { ProcessStartInfo start = new ProcessStartInfo(); start.FileName = fileName; @@ -128,7 +128,7 @@ namespace Flow.Launcher.Core.Plugin start.CreateNoWindow = true; start.RedirectStandardOutput = true; start.RedirectStandardError = true; - return await ExecuteAsync(start, token); + return ExecuteAsync(start, token); } protected string Execute(ProcessStartInfo startInfo) From 34a7a1522642f4252e6106511cb8538508bfdfa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 13:56:27 +0800 Subject: [PATCH 035/398] Use Stream model --- Flow.Launcher.Core/Plugin/ExecutablePlugin.cs | 3 +- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 141 ++++++++++-------- Flow.Launcher.Core/Plugin/PythonPlugin.cs | 2 +- 3 files changed, 78 insertions(+), 68 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs index d0d47aa58..13b6ac968 100644 --- a/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs +++ b/Flow.Launcher.Core/Plugin/ExecutablePlugin.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.IO; using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Plugin; @@ -23,7 +24,7 @@ namespace Flow.Launcher.Core.Plugin }; } - protected override Task ExecuteQueryAsync(Query query, CancellationToken token) + protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index a37b2b68b..a5633b118 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Reflection; using System.Text.Json; using System.Threading; @@ -26,7 +27,7 @@ namespace Flow.Launcher.Core.Plugin /// public abstract string SupportedLanguage { get; set; } - protected abstract Task ExecuteQueryAsync(Query query, CancellationToken token); + protected abstract Task ExecuteQueryAsync(Query query, CancellationToken token); protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); protected abstract string ExecuteContextMenu(Result selectedResult); @@ -45,53 +46,66 @@ namespace Flow.Launcher.Core.Plugin } } - private List DeserializedResult(string output) + + + private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) { - if (!String.IsNullOrEmpty(output)) + var results = new List(); + if (queryResponseModel.Result == null) return null; + + foreach (JsonRPCResult result in queryResponseModel.Result) { - List results = new List(); - - JsonRPCQueryResponseModel queryResponseModel = - JsonSerializer.Deserialize(output); - if (queryResponseModel.Result == null) return null; - - foreach (JsonRPCResult result in queryResponseModel.Result) + result.Action = c => { - result.Action = c => - { - if (result.JsonRPCAction == null) return false; + if (result.JsonRPCAction == null) return false; - if (!String.IsNullOrEmpty(result.JsonRPCAction.Method)) + if (!string.IsNullOrEmpty(result.JsonRPCAction.Method)) + { + if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) { - if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) + ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4), + result.JsonRPCAction.Parameters); + } + else + { + string actionReponse = ExecuteCallback(result.JsonRPCAction); + JsonRPCRequestModel jsonRpcRequestModel = + JsonSerializer.Deserialize(actionReponse); + if (jsonRpcRequestModel != null + && !string.IsNullOrEmpty(jsonRpcRequestModel.Method) + && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) { - ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4), - result.JsonRPCAction.Parameters); - } - else - { - string actionReponse = ExecuteCallback(result.JsonRPCAction); - JsonRPCRequestModel jsonRpcRequestModel = - JsonSerializer.Deserialize(actionReponse); - if (jsonRpcRequestModel != null - && !String.IsNullOrEmpty(jsonRpcRequestModel.Method) - && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) - { - ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), - jsonRpcRequestModel.Parameters); - } + ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), + jsonRpcRequestModel.Parameters); } } + } - return !result.JsonRPCAction.DontHideAfterAction; - }; - results.Add(result); - } - - return results; + return !result.JsonRPCAction.DontHideAfterAction; + }; + results.Add(result); } - return null; + return results; + } + + private async Task> DeserializedResultAsync(Stream output) + { + if (output == Stream.Null) return null; + + JsonRPCQueryResponseModel queryResponseModel = await + JsonSerializer.DeserializeAsync(output); + + return ParseResults(queryResponseModel); + } + + private List DeserializedResult(string output) + { + if (string.IsNullOrEmpty(output)) return null; + + JsonRPCQueryResponseModel queryResponseModel = + JsonSerializer.Deserialize(output); + return ParseResults(queryResponseModel); } private void ExecuteFlowLauncherAPI(string method, object[] parameters) @@ -119,15 +133,17 @@ namespace Flow.Launcher.Core.Plugin /// /// Cancellation Token /// - protected Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) + protected Task ExecuteAsync(string fileName, string arguments, CancellationToken token = default) { - ProcessStartInfo start = new ProcessStartInfo(); - start.FileName = fileName; - start.Arguments = arguments; - start.UseShellExecute = false; - start.CreateNoWindow = true; - start.RedirectStandardOutput = true; - start.RedirectStandardError = true; + ProcessStartInfo start = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + }; return ExecuteAsync(start, token); } @@ -137,7 +153,7 @@ namespace Flow.Launcher.Core.Plugin { using var process = Process.Start(startInfo); if (process == null) return string.Empty; - + using var standardOutput = process.StandardOutput; var result = standardOutput.ReadToEnd(); @@ -157,12 +173,12 @@ namespace Flow.Launcher.Core.Plugin if (result.StartsWith("DEBUG:")) { - MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); + MessageBox.Show(new Form { TopMost = true }, result.Substring(6)); return string.Empty; } return result; - + } catch (Exception e) { @@ -173,7 +189,7 @@ namespace Flow.Launcher.Core.Plugin } } - protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) + protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) { try { @@ -181,17 +197,14 @@ namespace Flow.Launcher.Core.Plugin if (process == null) { Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process"); - return string.Empty; + return Stream.Null; } - using var standardOutput = process.StandardOutput; + var result = process.StandardOutput.BaseStream; token.ThrowIfCancellationRequested(); - var result = await standardOutput.ReadToEndAsync(); - token.ThrowIfCancellationRequested(); - - if (string.IsNullOrEmpty(result)) + if (!process.StandardError.EndOfStream) { using var standardError = process.StandardError; var error = await standardError.ReadToEndAsync(); @@ -200,17 +213,11 @@ namespace Flow.Launcher.Core.Plugin if (!string.IsNullOrEmpty(error)) { Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}"); - return string.Empty; + return Stream.Null; } Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error."); - return string.Empty; - } - - if (result.StartsWith("DEBUG:")) - { - MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); - return string.Empty; + return Stream.Null; } return result; @@ -220,16 +227,18 @@ namespace Flow.Launcher.Core.Plugin Log.Exception( $"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e); - return string.Empty; + return Stream.Null; } } + + public async Task> QueryAsync(Query query, CancellationToken token) { - string output = await ExecuteQueryAsync(query, token); + var output = await ExecuteQueryAsync(query, token); try { - return DeserializedResult(output); + return await DeserializedResultAsync(output); } catch (Exception e) { diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 750684954..356a68a81 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -30,7 +30,7 @@ namespace Flow.Launcher.Core.Plugin } - protected override Task ExecuteQueryAsync(Query query, CancellationToken token) + protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { From 824ea07ab2ca46c6563563708371f1af2d2a7f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 13:59:59 +0800 Subject: [PATCH 036/398] add Debug message field for debugging --- Flow.Launcher.Core/Plugin/JsonPRCModel.cs | 2 + Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 43 ++++++++++++---------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs index a0731f8fb..b1c8ff6ea 100644 --- a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs +++ b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs @@ -45,6 +45,8 @@ namespace Flow.Launcher.Core.Plugin { [JsonPropertyName("result")] public new List Result { get; set; } + + public string DebugMessage { get; set; } } public class JsonRPCRequestModel : JsonRPCModelBase diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index a5633b118..e342a5f94 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -48,11 +48,35 @@ namespace Flow.Launcher.Core.Plugin + private async Task> DeserializedResultAsync(Stream output) + { + if (output == Stream.Null) return null; + + JsonRPCQueryResponseModel queryResponseModel = await + JsonSerializer.DeserializeAsync(output); + + return ParseResults(queryResponseModel); + } + + private List DeserializedResult(string output) + { + if (string.IsNullOrEmpty(output)) return null; + + JsonRPCQueryResponseModel queryResponseModel = + JsonSerializer.Deserialize(output); + return ParseResults(queryResponseModel); + } + private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) { var results = new List(); if (queryResponseModel.Result == null) return null; + if(!string.IsNullOrEmpty(queryResponseModel.DebugMessage)) + { + context.API.ShowMsg(queryResponseModel.DebugMessage); + } + foreach (JsonRPCResult result in queryResponseModel.Result) { result.Action = c => @@ -89,25 +113,6 @@ namespace Flow.Launcher.Core.Plugin return results; } - private async Task> DeserializedResultAsync(Stream output) - { - if (output == Stream.Null) return null; - - JsonRPCQueryResponseModel queryResponseModel = await - JsonSerializer.DeserializeAsync(output); - - return ParseResults(queryResponseModel); - } - - private List DeserializedResult(string output) - { - if (string.IsNullOrEmpty(output)) return null; - - JsonRPCQueryResponseModel queryResponseModel = - JsonSerializer.Deserialize(output); - return ParseResults(queryResponseModel); - } - private void ExecuteFlowLauncherAPI(string method, object[] parameters) { MethodInfo methodInfo = PluginManager.API.GetType().GetMethod(method); From f9b8216e3b9e9021e5ea0eaee39bea0eae3837d1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 14 Feb 2021 19:00:00 +1100 Subject: [PATCH 037/398] add copy bookmark's url to clipboard --- .../Images/copylink.png | Bin 0 -> 10725 bytes .../Languages/en.xaml | 2 + .../Main.cs | 47 ++++++++++++++++-- .../plugin.json | 2 +- 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Images/copylink.png new file mode 100644 index 0000000000000000000000000000000000000000..0dee870b615bc9caddeff44be4096b4b5f6fa92e GIT binary patch literal 10725 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 tab Set browser from path: Choose + Copy url + Copy the bookmark's url to clipboard \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 47493654f..3726f1683 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Windows; using System.Windows.Controls; +using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.BrowserBookmark.Commands; using Flow.Launcher.Plugin.BrowserBookmark.Models; @@ -9,7 +12,7 @@ using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.BrowserBookmark { - public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable + public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable, IContextMenu { private PluginInitContext context; @@ -60,7 +63,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark } return true; - } + }, + ContextData = new BookmarkAttributes { Url = c.Url } }).Where(r => r.Score > 0); return returnList.ToList(); } @@ -84,7 +88,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark } return true; - } + }, + ContextData = new BookmarkAttributes { Url = c.Url } }).ToList(); } } @@ -115,5 +120,39 @@ namespace Flow.Launcher.Plugin.BrowserBookmark { _storage.Save(); } + + public List LoadContextMenus(Result selectedResult) + { + return new List() { + new Result + { + Title = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"), + SubTitle = context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"), + Action = _ => + { + try + { + Clipboard.SetText(((BookmarkAttributes)selectedResult.ContextData).Url); + + return true; + } + catch (Exception e) + { + var message = "Failed to set url in clipboard"; + Log.Exception("Main",message, e, "LoadContextMenus"); + + context.API.ShowMsg(message); + + return false; + } + }, + IcoPath = "Images\\copylink.png" + }}; + } + + internal class BookmarkAttributes + { + internal string Url { get; set; } + } } } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index b0c3d2e29..62311f514 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -4,7 +4,7 @@ "Name": "Browser Bookmarks", "Description": "Search your browser bookmarks", "Author": "qianlifeng, Ioannis G.", - "Version": "1.3.2", + "Version": "1.4.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll", From 93f4b6d90adaf017a7e0df5a2dd6e8172a2686ef Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 14 Feb 2021 20:32:20 +1100 Subject: [PATCH 038/398] update to use SetDataObject --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 3726f1683..b889bb0d0 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -132,7 +132,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark { try { - Clipboard.SetText(((BookmarkAttributes)selectedResult.ContextData).Url); + Clipboard.SetDataObject(((BookmarkAttributes)selectedResult.ContextData).Url); return true; } From f8557da336171ddb738715d700e70dca4bf2a5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:08:30 +0800 Subject: [PATCH 039/398] add Logs, LoadJsonStorage, SaveJsonStorage to IPublicAPI --- .../Storage/JsonStorage.cs | 2 +- .../Storage/PluginJsonStorage.cs | 5 +++ Flow.Launcher.Plugin/IPublicAPI.cs | 12 ++++++ Flow.Launcher/PublicAPIInstance.cs | 39 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index f0e4a79fc..37cfec252 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Infrastructure.Storage public class JsonStrorage where T : new() { private readonly JsonSerializerOptions _serializerSettings; - private T _data; + protected T _data; // need a new directory name public const string DirectoryName = "Settings"; public const string FileSuffix = ".json"; diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index 5418e1837..ca7c454c4 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -15,5 +15,10 @@ namespace Flow.Launcher.Infrastructure.Storage FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); } + + public PluginJsonStorage(T data) : this() + { + _data = data; + } } } diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index dd73eb0e5..2a69a2495 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -100,5 +101,16 @@ namespace Flow.Launcher.Plugin void RemoveActionKeyword(string pluginId, string oldActionKeyword); + void LogDebug(string className, string message, [CallerMemberName] string methodName = ""); + + void LogInfo(string className, string message, [CallerMemberName] string methodName = ""); + + void LogWarn(string className, string message, [CallerMemberName] string methodName = ""); + + void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = ""); + + T LoadJsonStorage(PluginMetadata metadata) where T : new(); + + void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new(); } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 0afa67d1a..9c36a709e 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -18,6 +18,9 @@ using System.Threading; using System.IO; using Flow.Launcher.Infrastructure.Http; using JetBrains.Annotations; +using System.Runtime.CompilerServices; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Storage; namespace Flow.Launcher { @@ -160,6 +163,41 @@ namespace Flow.Launcher { PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); } + + + public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") + { + Log.Debug(className, message, methodName); + } + + public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") + { + Log.Info(className, message, methodName); + } + + public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") + { + Log.Warn(className, message, methodName); + } + + public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") + { + Log.Exception(className, message, e, methodName); + } + + public T LoadJsonStorage(PluginMetadata metadata) where T : new() + { + var storage = new PluginJsonStorage(); + return storage.Load(); + } + + public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() + { + var storage = new PluginJsonStorage(setting); + storage.Save(); + } + + #endregion #region Private Methods @@ -173,6 +211,7 @@ namespace Flow.Launcher return true; } + #endregion } } From 9fb44e6002d62227ffef1828c6e33c85a2ad6758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:12:57 +0800 Subject: [PATCH 040/398] format code --- Flow.Launcher/PublicAPIInstance.cs | 97 ++++++------------------------ 1 file changed, 19 insertions(+), 78 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 9c36a709e..967cf1bf7 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -70,15 +70,9 @@ namespace Flow.Launcher UpdateManager.RestartApp(Constant.ApplicationFileName); } - public void RestarApp() - { - RestartApp(); - } + public void RestarApp() => RestartApp(); - public void CheckForNewUpdate() - { - _settingsVM.UpdateApp(); - } + public void CheckForNewUpdate() => _settingsVM.UpdateApp(); public void SaveAppAllSettings() { @@ -88,15 +82,9 @@ namespace Flow.Launcher ImageLoader.Save(); } - public Task ReloadAllPluginData() - { - return PluginManager.ReloadData(); - } + public Task ReloadAllPluginData() => PluginManager.ReloadData(); - public void ShowMsg(string title, string subTitle = "", string iconPath = "") - { - ShowMsg(title, subTitle, iconPath, true); - } + public void ShowMsg(string title, string subTitle = "", string iconPath = "") => ShowMsg(title, subTitle, iconPath, true); public void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true) { @@ -115,87 +103,40 @@ namespace Flow.Launcher }); } - public void StartLoadingBar() - { - _mainVM.ProgressBarVisibility = Visibility.Visible; - } + public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible; - public void StopLoadingBar() - { - _mainVM.ProgressBarVisibility = Visibility.Collapsed; - } + public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed; - public string GetTranslation(string key) - { - return InternationalizationManager.Instance.GetTranslation(key); - } + public string GetTranslation(string key) => InternationalizationManager.Instance.GetTranslation(key); - public List GetAllPlugins() - { - return PluginManager.AllPlugins.ToList(); - } + public List GetAllPlugins() => PluginManager.AllPlugins.ToList(); public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; public MatchResult FuzzySearch(string query, string stringToCompare) => StringMatcher.FuzzySearch(query, stringToCompare); - public Task HttpGetStringAsync(string url, CancellationToken token = default) - { - return Http.GetAsync(url); - } + public Task HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url); - public Task HttpGetStreamAsync(string url, CancellationToken token = default) - { - return Http.GetStreamAsync(url); - } + public Task HttpGetStreamAsync(string url, CancellationToken token = default) => Http.GetStreamAsync(url); - public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath) - { - return Http.DownloadAsync(url, filePath); - } + public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath) => Http.DownloadAsync(url, filePath); - public void AddActionKeyword(string pluginId, string newActionKeyword) - { - PluginManager.AddActionKeyword(pluginId, newActionKeyword); - } + public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword); - public void RemoveActionKeyword(string pluginId, string oldActionKeyword) - { - PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); - } + public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); - public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") - { - Log.Debug(className, message, methodName); - } + public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") => Log.Debug(className, message, methodName); - public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") - { - Log.Info(className, message, methodName); - } + public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName); - public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") - { - Log.Warn(className, message, methodName); - } + public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") => Log.Warn(className, message, methodName); - public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") - { - Log.Exception(className, message, e, methodName); - } + public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName); - public T LoadJsonStorage(PluginMetadata metadata) where T : new() - { - var storage = new PluginJsonStorage(); - return storage.Load(); - } + public T LoadJsonStorage(PluginMetadata metadata) where T : new() => new PluginJsonStorage().Load(); - public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() - { - var storage = new PluginJsonStorage(setting); - storage.Save(); - } + public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() => new PluginJsonStorage(setting).Save(); #endregion From 71ca3bbf38b4fd55aca0540530d812d6df9d7011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:15:46 +0800 Subject: [PATCH 041/398] formatting --- Flow.Launcher/PublicAPIInstance.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 967cf1bf7..327ee6658 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -111,8 +111,6 @@ namespace Flow.Launcher public List GetAllPlugins() => PluginManager.AllPlugins.ToList(); - public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; - public MatchResult FuzzySearch(string query, string stringToCompare) => StringMatcher.FuzzySearch(query, stringToCompare); public Task HttpGetStringAsync(string url, CancellationToken token = default) => Http.GetAsync(url); @@ -125,7 +123,6 @@ namespace Flow.Launcher public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); - public void LogDebug(string className, string message, [CallerMemberName] string methodName = "") => Log.Debug(className, message, methodName); public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName); @@ -138,6 +135,7 @@ namespace Flow.Launcher public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() => new PluginJsonStorage(setting).Save(); + public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; #endregion From 8a26471c4fca63ce8b413d69ce3f32fd1849f389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 18:25:09 +0800 Subject: [PATCH 042/398] Update Document and remove unused parameter --- Flow.Launcher.Plugin/IPublicAPI.cs | 61 +++++++++++++++++++++++++++++- Flow.Launcher/PublicAPIInstance.cs | 4 +- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index 2a69a2495..a3f711a17 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -89,28 +89,85 @@ namespace Flow.Launcher.Plugin /// event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; + /// + /// Fuzzy Search the string with query + /// + /// Query String + /// The string to Search for Query + /// Match results MatchResult FuzzySearch(string query, string stringToCompare); + /// + /// Http Get to the spefic URL + /// + /// URL to call Http Get + /// Cancellation Token + /// Task to get string result Task HttpGetStringAsync(string url, CancellationToken token = default); + /// + /// Http Get to the spefic URL + /// + /// URL to call Http Get + /// Cancellation Token + /// Task to get stream result Task HttpGetStreamAsync(string url, CancellationToken token = default); + /// + /// Download the specific url to a cretain file path + /// + /// URL to download file + /// place to store file + /// Task showing the progress Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath); + /// + /// Add ActionKeyword for specific plugin + /// + /// ID for plugin that needs to add action keyword + /// The actionkeyword that is supposed to be added void AddActionKeyword(string pluginId, string newActionKeyword); + /// + /// Remove ActionKeyword for specific plugin + /// + /// ID for plugin that needs to remove action keyword + /// The actionkeyword that is supposed to be removed void RemoveActionKeyword(string pluginId, string oldActionKeyword); + /// + /// Log Debug message + /// Message will only be logged in Debug mode + /// void LogDebug(string className, string message, [CallerMemberName] string methodName = ""); + /// + /// Log Message + /// void LogInfo(string className, string message, [CallerMemberName] string methodName = ""); + /// + /// Log Warning + /// void LogWarn(string className, string message, [CallerMemberName] string methodName = ""); + /// + /// Log an Exception + /// void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = ""); - T LoadJsonStorage(PluginMetadata metadata) where T : new(); + /// + /// Load JsonStorage for current plugin + /// + /// Type for deserialization + /// + T LoadJsonStorage() where T : new(); - void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new(); + /// + /// Save JsonStorage for current plugin + /// + /// Type for Serialization + /// + void SaveJsonStorage(T setting) where T : new(); } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 327ee6658..200eb166a 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -131,9 +131,9 @@ namespace Flow.Launcher public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName); - public T LoadJsonStorage(PluginMetadata metadata) where T : new() => new PluginJsonStorage().Load(); + public T LoadJsonStorage() where T : new() => new PluginJsonStorage().Load(); - public void SaveJsonStorage(PluginMetadata metadata, T setting) where T : new() => new PluginJsonStorage(setting).Save(); + public void SaveJsonStorage(T setting) where T : new() => new PluginJsonStorage(setting).Save(); public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; From 1ec069bfcf1a982de6a2222bc70bcfe8eebfdf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 22:36:05 +0800 Subject: [PATCH 043/398] Use HttpCompletionOption.ResponseHeadersRead for Stream reading and downloading --- Flow.Launcher.Infrastructure/Http/Http.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index de2e82359..3a3e770a5 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -75,7 +75,7 @@ namespace Flow.Launcher.Infrastructure.Http { try { - using var response = await client.GetAsync(url, token); + using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); if (response.StatusCode == HttpStatusCode.OK) { await using var fileStream = new FileStream(filePath, FileMode.CreateNew); @@ -135,7 +135,7 @@ namespace Flow.Launcher.Infrastructure.Http public static async Task GetStreamAsync([NotNull] string url, CancellationToken token = default) { Log.Debug($"|Http.Get|Url <{url}>"); - var response = await client.GetAsync(url, token); + var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); return await response.Content.ReadAsStreamAsync(); } } From 20c0b1788ffda2c5ac38589f828912cb5bc80137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 14 Feb 2021 22:43:11 +0800 Subject: [PATCH 044/398] Add Cancellation to IPublicAPI download method --- Flow.Launcher.Plugin/IPublicAPI.cs | 2 +- Flow.Launcher/PublicAPIInstance.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index a3f711a17..27a65191a 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -119,7 +119,7 @@ namespace Flow.Launcher.Plugin /// URL to download file /// place to store file /// Task showing the progress - Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath); + Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default); /// public static void UpdateProxy(ProxyProperty property) { - (WebProxy.Address, WebProxy.Credentials) = property switch + if (string.IsNullOrEmpty(Proxy.Server)) + return; + + try { - ProxyProperty.Enabled => Proxy.Enabled switch + (WebProxy.Address, WebProxy.Credentials) = property switch { - true => Proxy.UserName switch + ProxyProperty.Enabled => Proxy.Enabled switch { - var userName when !string.IsNullOrEmpty(userName) => - (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null), - _ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), - new NetworkCredential(Proxy.UserName, Proxy.Password)) + true when !string.IsNullOrEmpty(Proxy.Server) => Proxy.UserName switch + { + var userName when string.IsNullOrEmpty(userName) => + (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null), + _ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), + new NetworkCredential(Proxy.UserName, Proxy.Password)) + }, + _ => (null, null) }, - false => (null, null) - }, - ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials), - ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials), - ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), - ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), - _ => throw new ArgumentOutOfRangeException() - }; + ProxyProperty.Server => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials), + ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials), + ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), + ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)), + _ => throw new ArgumentOutOfRangeException() + }; + } + catch(UriFormatException e) + { + Log.Exception("Http", "Unable to parse Uri", e); + } } public static async Task DownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default) From cfbdf294bbbac33ff5232e6d19f59853df7ebf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 22 Feb 2021 12:11:02 +0800 Subject: [PATCH 094/398] Add api to Http.cs & message shown up when error thrown --- Flow.Launcher.Infrastructure/Http/Http.cs | 8 +++++++- Flow.Launcher/App.xaml.cs | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index e8cf39880..c9b2a6877 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -9,6 +9,8 @@ using Flow.Launcher.Infrastructure.UserSettings; using System; using System.ComponentModel; using System.Threading; +using System.Windows.Interop; +using Flow.Launcher.Plugin; namespace Flow.Launcher.Infrastructure.Http { @@ -18,6 +20,8 @@ namespace Flow.Launcher.Infrastructure.Http private static HttpClient client = new HttpClient(); + public static IPublicAPI _api { get; set; } + static Http() { // need to be added so it would work on a win10 machine @@ -77,7 +81,9 @@ namespace Flow.Launcher.Infrastructure.Http } catch(UriFormatException e) { - Log.Exception("Http", "Unable to parse Uri", e); + _api.ShowMsg("Please try again", "Unable to parse Http Proxy"); + Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e); + } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 7c4c6a367..54ede7ff7 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -61,7 +61,6 @@ namespace Flow.Launcher _settingsVM = new SettingWindowViewModel(_updater, _portable); _settings = _settingsVM.Settings; - Http.Proxy = _settings.Proxy; _alphabet.Initialize(_settings); _stringMatcher = new StringMatcher(_alphabet); @@ -74,6 +73,10 @@ namespace Flow.Launcher await PluginManager.InitializePlugins(API); var window = new MainWindow(_settings, _mainVM); + Http._api = API; + Http.Proxy = _settings.Proxy; + + Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Current.MainWindow = window; From 0981be499fb2816bae496fa6412cc627ae080c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 22 Feb 2021 13:16:48 +0800 Subject: [PATCH 095/398] Use PortablePDB instead of Full pdb for debugging --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 2 +- .../Flow.Launcher.Infrastructure.csproj | 2 +- Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj | 2 +- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 2 +- Flow.Launcher/Flow.Launcher.csproj | 2 +- .../Flow.Launcher.Plugin.BrowserBookmark.csproj | 2 +- .../Flow.Launcher.Plugin.Calculator.csproj | 2 +- .../Flow.Launcher.Plugin.ControlPanel.csproj | 2 +- .../Flow.Launcher.Plugin.PluginIndicator.csproj | 2 +- .../Flow.Launcher.Plugin.ProcessKiller.csproj | 2 +- .../Flow.Launcher.Plugin.Program.csproj | 2 +- .../Flow.Launcher.Plugin.Shell.csproj | 2 +- .../Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj | 2 +- .../Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj | 2 +- .../Flow.Launcher.Plugin.WebSearch.csproj | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index b0cc07e1a..4f1e58893 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -16,7 +16,7 @@ true - full + portable false ..\Output\Debug\ DEBUG;TRACE diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 8153de6c8..66e1766c8 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -15,7 +15,7 @@ true - full + portable false ..\Output\Debug\ DEBUG;TRACE diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 0eefe5c4f..fbcb46b1c 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -34,7 +34,7 @@ true - full + portable false ..\Output\Debug\ DEBUG;TRACE diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index e970c47b9..ba35c1c8d 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -15,7 +15,7 @@ true - full + portable false bin\Debug\ DEBUG;TRACE diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 20e847f82..196a651a4 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -17,7 +17,7 @@ AnyCPU true - full + portable false ..\Output\Debug\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index d2a8736a6..f5d0a817f 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -14,7 +14,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.BrowserBookmark\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index 1090926fc..14f88689b 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -16,7 +16,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Caculator\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj index 06969a135..19a66cc7f 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj @@ -14,7 +14,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.ControlPanel\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index cc280b9a9..7b043ad74 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -15,7 +15,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginIndicator\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index a643ebf86..9c86f5238 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -16,7 +16,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.ProcessKiller\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index a2d7b7fef..e4e4724d3 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -16,7 +16,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Program\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index d3042722b..10597f9e0 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -15,7 +15,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Shell\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index c25e759d3..11ceafb8f 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -15,7 +15,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Sys\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index 671a8b1c2..a4b2ac1f5 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -15,7 +15,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.Url\ DEBUG;TRACE diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index af86af842..0a2811f6d 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -15,7 +15,7 @@ true - full + portable false ..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.WebSearch\ DEBUG;TRACE From f940e695a1bb6591cc33f0aaabbb37c9dfec690b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 22 Feb 2021 13:45:28 +0800 Subject: [PATCH 096/398] add Unit test for Http.cs setting --- Flow.Launcher.Test/HttpTest.cs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Flow.Launcher.Test/HttpTest.cs diff --git a/Flow.Launcher.Test/HttpTest.cs b/Flow.Launcher.Test/HttpTest.cs new file mode 100644 index 000000000..96213aba6 --- /dev/null +++ b/Flow.Launcher.Test/HttpTest.cs @@ -0,0 +1,33 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Text; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Infrastructure.Http; + +namespace Flow.Launcher.Test +{ + [TestFixture] + class HttpTest + { + [Test] + public void TestSettingUpdate() + { + HttpProxy proxy = new HttpProxy(); + Http.Proxy = proxy; + + proxy.Enabled = true; + proxy.Server = "127.0.0.1"; + Assert.AreEqual(Http.WebProxy.Address, new Uri($"http://{proxy.Server}:{proxy.Port}")); + Assert.IsNull(Http.WebProxy.Credentials); + + proxy.UserName = "test"; + Assert.NotNull(Http.WebProxy.Credentials); + Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").UserName, proxy.UserName); + Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, ""); + + proxy.Password = "test password"; + Assert.AreEqual(Http.WebProxy.Credentials.GetCredential(Http.WebProxy.Address, "Basic").Password, proxy.Password); + } + } +} From d41f47ddcfe106946ed2d013b1d836298eafd6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 22 Feb 2021 13:45:40 +0800 Subject: [PATCH 097/398] Migrate UrlPluginTest to Plugins folder --- Flow.Launcher.Test/{ => Plugins}/UrlPluginTest.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Flow.Launcher.Test/{ => Plugins}/UrlPluginTest.cs (100%) diff --git a/Flow.Launcher.Test/UrlPluginTest.cs b/Flow.Launcher.Test/Plugins/UrlPluginTest.cs similarity index 100% rename from Flow.Launcher.Test/UrlPluginTest.cs rename to Flow.Launcher.Test/Plugins/UrlPluginTest.cs From b01e23bd0349af200a86a391b7ad29de567b1a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 22 Feb 2021 16:58:00 +0800 Subject: [PATCH 098/398] move http proxy initialization earlier than Plugin initialization --- Flow.Launcher/App.xaml.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 54ede7ff7..7c9697c07 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -70,11 +70,14 @@ namespace Flow.Launcher PluginManager.LoadPlugins(_settings.PluginSettings); _mainVM = new MainViewModel(_settings); API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet); - await PluginManager.InitializePlugins(API); - var window = new MainWindow(_settings, _mainVM); Http._api = API; Http.Proxy = _settings.Proxy; + + await PluginManager.InitializePlugins(API); + var window = new MainWindow(_settings, _mainVM); + + Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); From a507cbdc60296642ba6100fe7a7d168f804db2b5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Feb 2021 20:31:00 +1100 Subject: [PATCH 099/398] fix formatting --- Flow.Launcher.Infrastructure/Http/Http.cs | 2 +- Flow.Launcher/App.xaml.cs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index c9b2a6877..34d3f36f0 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -68,7 +68,7 @@ namespace Flow.Launcher.Infrastructure.Http var userName when string.IsNullOrEmpty(userName) => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null), _ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), - new NetworkCredential(Proxy.UserName, Proxy.Password)) + new NetworkCredential(Proxy.UserName, Proxy.Password)) }, _ => (null, null) }, diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 7c9697c07..56cd5b961 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -77,9 +77,6 @@ namespace Flow.Launcher await PluginManager.InitializePlugins(API); var window = new MainWindow(_settings, _mainVM); - - - Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Current.MainWindow = window; From 5f44663bd7dee878154e2596999d54ecb340fd0d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Feb 2021 20:33:11 +1100 Subject: [PATCH 100/398] remove space --- Flow.Launcher.Infrastructure/Http/Http.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 34d3f36f0..43f6237ae 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -83,7 +83,6 @@ namespace Flow.Launcher.Infrastructure.Http { _api.ShowMsg("Please try again", "Unable to parse Http Proxy"); Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e); - } } From 9192ab755077c1752cd8808c83cfb67bb7571e4c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Feb 2021 20:36:06 +1100 Subject: [PATCH 101/398] update variable naming convention --- Flow.Launcher.Infrastructure/Http/Http.cs | 4 ++-- Flow.Launcher/App.xaml.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 43f6237ae..b45b6adcd 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -20,7 +20,7 @@ namespace Flow.Launcher.Infrastructure.Http private static HttpClient client = new HttpClient(); - public static IPublicAPI _api { get; set; } + public static IPublicAPI API { get; set; } static Http() { @@ -81,7 +81,7 @@ namespace Flow.Launcher.Infrastructure.Http } catch(UriFormatException e) { - _api.ShowMsg("Please try again", "Unable to parse Http Proxy"); + API.ShowMsg("Please try again", "Unable to parse Http Proxy"); Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e); } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 56cd5b961..a2907c927 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -71,7 +71,7 @@ namespace Flow.Launcher _mainVM = new MainViewModel(_settings); API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet); - Http._api = API; + Http.API = API; Http.Proxy = _settings.Proxy; await PluginManager.InitializePlugins(API); From 99193a42d87d0c04efea6524530281e0c9c862a2 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Feb 2021 20:58:21 +1100 Subject: [PATCH 102/398] version bump Program plugin --- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index d110124ff..082f0d853 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.4.2", + "Version": "1.4.3", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", From 77efbffa933bafc7d39e041ef83442acb8d855bb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Feb 2021 21:27:36 +1100 Subject: [PATCH 103/398] update test name --- Flow.Launcher.Test/HttpTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Test/HttpTest.cs b/Flow.Launcher.Test/HttpTest.cs index 96213aba6..637747a07 100644 --- a/Flow.Launcher.Test/HttpTest.cs +++ b/Flow.Launcher.Test/HttpTest.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using System; using System.Collections.Generic; using System.Text; @@ -11,7 +11,7 @@ namespace Flow.Launcher.Test class HttpTest { [Test] - public void TestSettingUpdate() + public void GivenHttpProxy_WhenUpdated_ThenWebProxyShouldAlsoBeUpdatedToTheSame() { HttpProxy proxy = new HttpProxy(); Http.Proxy = proxy; From e2fa9683b93aca33b2517d4a55335510cc9da5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 23 Feb 2021 09:36:37 +0800 Subject: [PATCH 104/398] change taskcanceledexception to operationcanceledexception --- Flow.Launcher.Core/Updater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 8f0de798c..9f9c0c2d1 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -87,7 +87,7 @@ namespace Flow.Launcher.Core UpdateManager.RestartApp(Constant.ApplicationFileName); } } - catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e is TaskCanceledException) + catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e is OperationCanceledException) { Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"), From b07b1ddb311e57ad034eace2d12bc08fc52600d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 23 Feb 2021 09:53:17 +0800 Subject: [PATCH 105/398] Change TaskCanceledException in WebSearch plugin to operationCanceledException --- Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs | 3 +-- .../SuggestionSources/Baidu.cs | 2 +- .../SuggestionSources/Bing.cs | 2 +- .../SuggestionSources/Google.cs | 6 +++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index 4135bb59a..7eca5a568 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -125,8 +125,7 @@ namespace Flow.Launcher.Plugin.WebSearch { var suggestions = await source.Suggestions(keyword, token).ConfigureAwait(false); - if (token.IsCancellationRequested) - return null; + token.ThrowIfCancellationRequested(); var resultsFromSuggestion = suggestions?.Select(o => new Result { diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index 385a9f8b5..38a589797 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -25,7 +25,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources const string api = "http://suggestion.baidu.com/su?json=1&wd="; result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } - catch (TaskCanceledException) + catch (OperationCanceledException) { return null; } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index e505d78cf..10de86820 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -28,7 +28,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS"); } - catch (TaskCanceledException) + catch (OperationCanceledException) { return new List(); } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index c075f4d5d..5f1ecb31b 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -23,11 +23,11 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources const string api = "https://www.google.com/complete/search?output=chrome&q="; using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); - - json = await JsonDocument.ParseAsync(resultStream); + + json = await JsonDocument.ParseAsync(resultStream, cancellationToken: token); } - catch (TaskCanceledException) + catch (OperationCanceledException) { return new List(); } From fec4b5b1836fdbcbe0e451f549ad3a3e043d16ca Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 23 Feb 2021 21:05:35 +1100 Subject: [PATCH 106/398] spacing --- Flow.Launcher/App.xaml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index a2907c927..7b808dd54 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -61,7 +61,6 @@ namespace Flow.Launcher _settingsVM = new SettingWindowViewModel(_updater, _portable); _settings = _settingsVM.Settings; - _alphabet.Initialize(_settings); _stringMatcher = new StringMatcher(_alphabet); StringMatcher.Instance = _stringMatcher; From 5a6f7cf2ce9c9ebd4a3806c7cdddf67a6aa6ed88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 23 Feb 2021 18:12:27 +0800 Subject: [PATCH 107/398] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d712ccf6..90df9ab62 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,11 @@ Yes please, submit an issue to let us know. ## Developing/Debugging -Flow Launcher's target framework is .Net Core 5 +Flow Launcher's target framework is .Net 5 Install Visual Studio 2019 -Install .Net Core 5 SDK via Visual Studio installer or manually from [here](https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.103-windows-x64-installer) +Install .Net 5 SDK via Visual Studio installer or manually from [here](https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.103-windows-x64-installer) ## Documentation From 740b792251c1ceffe7d06d0370dfe941d6de5cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 23 Feb 2021 18:28:46 +0800 Subject: [PATCH 108/398] add Content back to csproj so that the xamls will be copied --- ...low.Launcher.Plugin.BrowserBookmark.csproj | 3 ++ .../Flow.Launcher.Plugin.WebSearch.csproj | 33 ++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index fc4547556..acfc79d7a 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -58,6 +58,9 @@ PreserveNewest + + PreserveNewest + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index b1660045c..3fbba1deb 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -5,7 +5,7 @@ net5.0-windows {403B57F2-1856-4FC7-8A24-36AB346B763E} Properties - true + true Flow.Launcher.Plugin.WebSearch Flow.Launcher.Plugin.WebSearch true @@ -13,7 +13,7 @@ false en - + true portable @@ -24,7 +24,7 @@ 4 false - + pdbonly true @@ -39,18 +39,21 @@ PreserveNewest - - - - + PreserveNewest - - + + - - - - - + + + PreserveNewest + + - \ No newline at end of file + + + + + + + \ No newline at end of file From 6aad061eb6bf26b76bbe50de9c25ed2aa3335aaa Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 23 Feb 2021 21:44:59 +1100 Subject: [PATCH 109/398] fix formatting --- .../Flow.Launcher.Plugin.WebSearch.csproj | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index 3fbba1deb..cd0bbdee3 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -42,18 +42,18 @@ PreserveNewest - + - - - PreserveNewest - - + + + PreserveNewest + + - - - - - + + + + + - \ No newline at end of file + \ No newline at end of file From 479bd757d43084fb2561ec4d1c7dc02fb60bfa5a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 24 Feb 2021 06:34:09 +1100 Subject: [PATCH 110/398] add comment --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 640c95ba0..0aebc9e34 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -192,6 +192,8 @@ namespace Flow.Launcher.Plugin.Program.Programs } catch { + // Bug from https://github.com/microsoft/CsWinRT, using Microsoft.Windows.SDK.NET.Ref 10.0.19041.0. + // Only happens on the first time, so a try catch can fix it. m = new PackageManager(); } var ps = m.FindPackagesForUser(id); From d81a4a7fa382f7c30fadf5180cf391cae6db8a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 12:37:57 +0800 Subject: [PATCH 111/398] avoid dupplicate assembly load --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 273698b86..c806f68bd 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Core.Plugin assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath)); referencedPluginPackageDependencyResolver = - new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.Plugin.dll")); + new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.dll")); } internal Assembly LoadAssemblyAndDependencies() From 6804b93a8ddc2a90eaceeea2af3b6b652f6b36ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 12:42:13 +0800 Subject: [PATCH 112/398] remove comment regarding newtonsoft.json --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index c806f68bd..1924c5a91 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -33,9 +33,6 @@ namespace Flow.Launcher.Core.Plugin { string assemblyPath = dependencyResolver.ResolveAssemblyToPath(assemblyName); - // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher.Plugin - // Otherwise will get unexpected behaviour with plugins, e.g. JsonIgnore attribute not honored in WebSearch or other plugins - // that use Newtonsoft.Json if (assemblyPath == null || ExistsInReferencedPluginPackage(assemblyName)) return null; From d161faebbb89c42fd2f51b8f0694595aab2ce966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 12:44:42 +0800 Subject: [PATCH 113/398] make referencedPluginPackageDependencyResolver static --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 1924c5a91..06f6eb22e 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -11,17 +11,22 @@ namespace Flow.Launcher.Core.Plugin { private readonly AssemblyDependencyResolver dependencyResolver; - private readonly AssemblyDependencyResolver referencedPluginPackageDependencyResolver; + private static readonly AssemblyDependencyResolver referencedPluginPackageDependencyResolver; private readonly AssemblyName assemblyName; + static PluginAssemblyLoader() + { + referencedPluginPackageDependencyResolver = + new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.dll")); + } + internal PluginAssemblyLoader(string assemblyFilePath) { dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath); assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath)); - referencedPluginPackageDependencyResolver = - new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.dll")); + } internal Assembly LoadAssemblyAndDependencies() From 0b51a8c467ba20328e6e4c48d46f74d73ecba373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 12:46:51 +0800 Subject: [PATCH 114/398] rename method --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 06f6eb22e..90363073b 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -38,7 +38,7 @@ namespace Flow.Launcher.Core.Plugin { string assemblyPath = dependencyResolver.ResolveAssemblyToPath(assemblyName); - if (assemblyPath == null || ExistsInReferencedPluginPackage(assemblyName)) + if (assemblyPath == null || ExistsInReferencedPackage(assemblyName)) return null; return LoadFromAssemblyPath(assemblyPath); @@ -51,7 +51,7 @@ namespace Flow.Launcher.Core.Plugin return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Intersect(types).Any()); } - internal bool ExistsInReferencedPluginPackage(AssemblyName assemblyName) + internal bool ExistsInReferencedPackage(AssemblyName assemblyName) { return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; } From 7739ac73fdd33f16a92f28f5251daace0640e056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 15:55:03 +0800 Subject: [PATCH 115/398] change exception handling in Updater and WebSearch suggestion source --- Flow.Launcher.Core/Updater.cs | 2 +- .../SuggestionSources/Baidu.cs | 13 +++++++------ .../SuggestionSources/Bing.cs | 10 +++++----- .../SuggestionSources/Google.cs | 10 +++++----- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 9f9c0c2d1..76713ce2a 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -87,7 +87,7 @@ namespace Flow.Launcher.Core UpdateManager.RestartApp(Constant.ApplicationFileName); } } - catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e is OperationCanceledException) + catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e.InnerException is TimeoutException) { Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"), diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index 38a589797..94458ff52 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -25,15 +25,16 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources const string api = "http://suggestion.baidu.com/su?json=1&wd="; result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); } - catch (OperationCanceledException) - { - return null; - } - catch (HttpRequestException e) + catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException) { Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); return null; } + catch (OperationCanceledException) + { + return null; + } + if (string.IsNullOrEmpty(result)) return new List(); Match match = _reg.Match(result); @@ -44,7 +45,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { json = JsonDocument.Parse(match.Groups[1].Value); } - catch(JsonException e) + catch (JsonException e) { Log.Exception("|Baidu.Suggestions|can't parse suggestions", e); return new List(); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 10de86820..28868100f 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -28,15 +28,15 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS"); } + catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException) + { + Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); + return null; + } catch (OperationCanceledException) { return new List(); } - catch (HttpRequestException e) - { - Log.Exception("|Bing.Suggestions|Can't get suggestion from Bing", e); - return new List(); - } catch (JsonException e) { Log.Exception("|Bing.Suggestions|can't parse suggestions", e); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 5f1ecb31b..a06deea29 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -27,15 +27,15 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources json = await JsonDocument.ParseAsync(resultStream, cancellationToken: token); } + catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException) + { + Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); + return null; + } catch (OperationCanceledException) { return new List(); } - catch (HttpRequestException e) - { - Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); - return new List(); - } catch (JsonException e) { Log.Exception("|Google.Suggestions|can't parse suggestions", e); From f3a1bcc01f154f5f2070b127bfd79dc953541975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 16:03:10 +0800 Subject: [PATCH 116/398] revise access of UserSelectedrecord, Histroy, TopMost setter access to private --- Flow.Launcher/Storage/QueryHistory.cs | 5 +++-- Flow.Launcher/Storage/TopMostRecord.cs | 12 ++---------- Flow.Launcher/Storage/UserSelectedRecord.cs | 11 ++--------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/Flow.Launcher/Storage/QueryHistory.cs b/Flow.Launcher/Storage/QueryHistory.cs index 2b2103605..a5383b179 100644 --- a/Flow.Launcher/Storage/QueryHistory.cs +++ b/Flow.Launcher/Storage/QueryHistory.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using Flow.Launcher.Plugin; +using System.Text.Json.Serialization; namespace Flow.Launcher.Storage { public class History { - public List Items { get; set; } = new List(); + [JsonInclude] + public List Items { get; private set; } = new List(); private int _maxHistory = 300; diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index c92ef4956..052c296cd 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using System.Linq; -using System.Text.Json; using System.Text.Json.Serialization; using Flow.Launcher.Plugin; @@ -9,14 +7,8 @@ namespace Flow.Launcher.Storage // todo this class is not thread safe.... but used from multiple threads. public class TopMostRecord { - /// - /// You should not directly access this field - /// - /// It is public due to System.Text.Json limitation in version 3.1 - /// - /// - /// TODO: Set it to private - public Dictionary records { get; set; } = new Dictionary(); + [JsonInclude] + public Dictionary records { get; private set; } = new Dictionary(); internal bool IsTopMost(Result result) { diff --git a/Flow.Launcher/Storage/UserSelectedRecord.cs b/Flow.Launcher/Storage/UserSelectedRecord.cs index bc7a2da73..fd3d87fc4 100644 --- a/Flow.Launcher/Storage/UserSelectedRecord.cs +++ b/Flow.Launcher/Storage/UserSelectedRecord.cs @@ -7,15 +7,8 @@ namespace Flow.Launcher.Storage { public class UserSelectedRecord { - /// - /// You should not directly access this field - /// - /// It is public due to System.Text.Json limitation in version 3.1 - /// - /// - /// TODO: Set it to private - [JsonPropertyName("records")] - public Dictionary records { get; set; } + [JsonInclude] + public Dictionary records { get; private set; } public UserSelectedRecord() { From 2a03bba6668026dc4d6e751925504b788cf2b2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 16:20:00 +0800 Subject: [PATCH 117/398] add using for JsonDocument in Google and Bing --- .../SuggestionSources/Bing.cs | 29 ++++++++++--------- .../SuggestionSources/Google.cs | 18 ++++++------ 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index e505d78cf..1a1f10d0c 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -17,7 +17,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public override async Task> Suggestions(string query, CancellationToken token) { - JsonElement json; try { @@ -25,7 +24,20 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); - json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS"); + using var json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)); + var root = json.RootElement.GetProperty("AS"); + + if (root.GetProperty("FullResults").GetInt32() == 0) + return new List(); + + return root.GetProperty("Results") + .EnumerateArray() + .SelectMany(r => r.GetProperty("Suggests") + .EnumerateArray() + .Select(s => s.GetProperty("Txt").GetString())) + .ToList(); + + } catch (TaskCanceledException) @@ -41,18 +53,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { Log.Exception("|Bing.Suggestions|can't parse suggestions", e); return new List(); - } - - if (json.GetProperty("FullResults").GetInt32() == 0) - return new List(); - - return json.GetProperty("Results") - .EnumerateArray() - .SelectMany(r => r.GetProperty("Suggests") - .EnumerateArray() - .Select(s => s.GetProperty("Txt").GetString())) - .ToList(); - + } } public override string ToString() diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index c075f4d5d..4ca9b2ede 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -16,15 +16,20 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources { public override async Task> Suggestions(string query, CancellationToken token) { - JsonDocument json; - try { const string api = "https://www.google.com/complete/search?output=chrome&q="; using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); - - json = await JsonDocument.ParseAsync(resultStream); + + using var json = await JsonDocument.ParseAsync(resultStream); + + if (json == null) + return new List(); + + var results = json.RootElement.EnumerateArray().ElementAt(1); + + return results.EnumerateArray().Select(o => o.GetString()).ToList(); } catch (TaskCanceledException) @@ -41,11 +46,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources Log.Exception("|Google.Suggestions|can't parse suggestions", e); return new List(); } - - var results = json?.RootElement.EnumerateArray().ElementAt(1); - - return results?.EnumerateArray().Select(o => o.GetString()).ToList() ?? new List(); - } public override string ToString() From 03df6e458532c3dff31b3615b7c10ac128c9c2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 18:49:01 +0800 Subject: [PATCH 118/398] add comment back and remove extra empty line --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 90363073b..0284afe5b 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -38,6 +38,10 @@ namespace Flow.Launcher.Core.Plugin { string assemblyPath = dependencyResolver.ResolveAssemblyToPath(assemblyName); + // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher + // Otherwise duplicate assembly will be loaded, and some weird behavior will occur such as WinRT.dll + // will fail to create + if (assemblyPath == null || ExistsInReferencedPackage(assemblyName)) return null; From 9e343ebff47c78a4f078ac3bcbb92a0d4a8d580f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 18:51:44 +0800 Subject: [PATCH 119/398] remove extra line --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 0284afe5b..91fc90964 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -25,8 +25,6 @@ namespace Flow.Launcher.Core.Plugin { dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath); assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath)); - - } internal Assembly LoadAssemblyAndDependencies() @@ -60,4 +58,4 @@ namespace Flow.Launcher.Core.Plugin return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; } } -} +} \ No newline at end of file From 84257aa5d9727cae8935631cf661ff7f74e031cc Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 24 Feb 2021 21:57:18 +1100 Subject: [PATCH 120/398] update PluginAssemblyLoader comment --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 91fc90964..22e3cc51a 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -36,10 +36,10 @@ namespace Flow.Launcher.Core.Plugin { string assemblyPath = dependencyResolver.ResolveAssemblyToPath(assemblyName); - // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher - // Otherwise duplicate assembly will be loaded, and some weird behavior will occur such as WinRT.dll - // will fail to create - + // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher. + // Otherwise duplicate assembly will be loaded and some weird behavior will occur, such as WinRT.Runtime.dll + // will fail due to loading multiple versions in process, each with their own static instance of registration state + if (assemblyPath == null || ExistsInReferencedPackage(assemblyName)) return null; From 81926c04cd709d25dbacd234c19548d588358350 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 24 Feb 2021 22:01:11 +1100 Subject: [PATCH 121/398] remove extra space --- Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 22e3cc51a..4ac4b2551 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -36,10 +36,9 @@ namespace Flow.Launcher.Core.Plugin { string assemblyPath = dependencyResolver.ResolveAssemblyToPath(assemblyName); - // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher. + // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher // Otherwise duplicate assembly will be loaded and some weird behavior will occur, such as WinRT.Runtime.dll - // will fail due to loading multiple versions in process, each with their own static instance of registration state - + // will fail due to loading multiple versions in process, each with their own static instance of registration state if (assemblyPath == null || ExistsInReferencedPackage(assemblyName)) return null; From a4b7bc522174aa6373ed2cef832525cee1ba8a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 24 Feb 2021 20:36:32 +0800 Subject: [PATCH 122/398] Use AppDomain to retrive the assembly that has been loaded, which contains the version --- .../Plugin/PluginAssemblyLoader.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index 91fc90964..e1c6d6d5a 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -1,5 +1,6 @@ using Flow.Launcher.Infrastructure; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -11,14 +12,17 @@ namespace Flow.Launcher.Core.Plugin { private readonly AssemblyDependencyResolver dependencyResolver; - private static readonly AssemblyDependencyResolver referencedPluginPackageDependencyResolver; - private readonly AssemblyName assemblyName; + private static readonly List loadedAssembly; + static PluginAssemblyLoader() { - referencedPluginPackageDependencyResolver = - new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.dll")); + loadedAssembly = new List(AppDomain.CurrentDomain.GetAssemblies()); + AppDomain.CurrentDomain.AssemblyLoad += (sender, args) => + { + loadedAssembly.Add(args.LoadedAssembly); + }; } internal PluginAssemblyLoader(string assemblyFilePath) @@ -39,7 +43,7 @@ namespace Flow.Launcher.Core.Plugin // When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher // Otherwise duplicate assembly will be loaded, and some weird behavior will occur such as WinRT.dll // will fail to create - + if (assemblyPath == null || ExistsInReferencedPackage(assemblyName)) return null; @@ -49,13 +53,14 @@ namespace Flow.Launcher.Core.Plugin internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, params Type[] types) { var allTypes = assembly.ExportedTypes; - return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Intersect(types).Any()); } internal bool ExistsInReferencedPackage(AssemblyName assemblyName) { - return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; + if (loadedAssembly.Any(a => a.FullName == assemblyName.FullName)) + return true; + return false; } } } \ No newline at end of file From 3a45c99c2916be4edb89f723c7e3ad56f1517e86 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 25 Feb 2021 13:59:29 +1100 Subject: [PATCH 123/398] version bump WebSearch plugin --- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index 6e9986749..996c9b686 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -25,7 +25,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.3.2", + "Version": "1.3.3", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From f9fa3ddd4686b5acd11455e2e47b4e5168137e41 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 25 Feb 2021 14:12:25 +1100 Subject: [PATCH 124/398] version bump WebSearch plugin --- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index 6e9986749..996c9b686 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -25,7 +25,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.3.2", + "Version": "1.3.3", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From ee75c6336ef129a23cd605c24be774404e1d9a7f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 25 Feb 2021 14:18:11 +1100 Subject: [PATCH 125/398] version bump WebSearch plugin after merge --- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index 996c9b686..e9f237a58 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -25,7 +25,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.3.3", + "Version": "1.3.4", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From cef09b3ee29df2310d409307224e7c4231aeddca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 26 Feb 2021 16:20:47 +0800 Subject: [PATCH 126/398] add e to calculator plugin, and fix a NPE for debugging Co-Authored-By: Pavel Zwerschke --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index 5b23ceacc..ab8ce892a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -32,7 +32,13 @@ namespace Flow.Launcher.Plugin.Caculator static Main() { - MagesEngine = new Engine(); + MagesEngine = new Engine(new Configuration + { + Scope = new Dictionary + { + { "e", Math.E }, // e is not contained in the default mages engine + } + }); } public void Init(PluginInitContext context) @@ -55,7 +61,7 @@ namespace Flow.Launcher.Plugin.Caculator var expression = query.Search.Replace(",", "."); var result = MagesEngine.Interpret(expression); - if (result.ToString() == "NaN") + if (result?.ToString() == "NaN") result = Context.API.GetTranslation("flowlauncher_plugin_calculator_not_a_number"); if (result is Function) From 66eaa0a2cd207398a8cda1332920789e073c1f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 26 Feb 2021 18:12:00 +0800 Subject: [PATCH 127/398] version bump --- Plugins/Flow.Launcher.Plugin.Calculator/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json index 7d9ca58d5..9bf532e87 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json @@ -4,7 +4,7 @@ "Name": "Calculator", "Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)", "Author": "cxfksword", - "Version": "1.1.4", + "Version": "1.1.5", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll", From b9cade708a6a4b8e97a93d352b22a3ee3de8781b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 26 Feb 2021 23:11:59 +1100 Subject: [PATCH 128/398] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 90df9ab62..6b5bcdac0 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be - Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse. - Search for file contents. +- Do mathematical calculations and copy the result to clipboard. - Support search using environment variable paths. - Run batch and PowerShell commands as Administrator or a different user. - Support languages from Chinese to Italian and more. From d65669cf04260f5f3a733a573dd46ee19a293de7 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 26 Feb 2021 23:17:32 +1100 Subject: [PATCH 129/398] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b5bcdac0..73ea53135 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Windows may complain about security due to code not being signed, this will be c - Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`. - The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`. - Start to launch your Python plugins. - - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). + - Flow searches files and contents via Windows Index Search, to use Everything: `pm install everything`. ### Usage - Open flow's search window: Alt+Space is the default hotkey. From 7ef3b0d93850f3b6d126656240c513d0c92420e6 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Mar 2021 07:44:45 +1100 Subject: [PATCH 130/398] change WebSearch default to global --- Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs | 2 +- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- Plugins/Flow.Launcher.Plugin.WebSearch/setting.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs index 8a1700415..33c304375 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Plugin.WebSearch new SearchSource { Title = "Google", - ActionKeyword = "g", + ActionKeyword = "*", Icon = "google.png", Url = "https://www.google.com/search?q={q}", Enabled = true diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index e9f237a58..d397379d8 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -1,7 +1,7 @@ { "ID": "565B73353DBF4806919830B9202EE3BF", "ActionKeywords": [ - "g", + "*", "wiki", "findicon", "facebook", diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json b/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json index c31d02ae6..ef9c63d67 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/setting.json @@ -2,7 +2,7 @@ "WebSearches": [ { "Title": "Google", - "ActionKeyword": "g", + "ActionKeyword": "*", "IconPath": "Images\\google.png", "Url": "https://www.google.com/search?q={q}", "Enabled": true From 901d508722cac60d2a5931f270f7df75a58c3bbe Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Mar 2021 07:51:46 +1100 Subject: [PATCH 131/398] version bump WebSearch --- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index d397379d8..c7c1e1591 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -25,7 +25,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.3.4", + "Version": "1.3.5", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From ab8a850349be0e0a2a205a92caea507761e68d63 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 1 Mar 2021 20:28:05 +1100 Subject: [PATCH 132/398] action keyword results appear above global action keyword results --- Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index 7eca5a568..5235f3fbe 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -25,6 +25,10 @@ namespace Flow.Launcher.Plugin.WebSearch internal static string DefaultImagesDirectory; internal static string CustomImagesDirectory; + private readonly int scoreStandard = 50; + + private readonly int scoreSuggestions = 48; + private readonly string SearchSourceGlobalPluginWildCardSign = "*"; public void Save() @@ -49,13 +53,17 @@ namespace Flow.Launcher.Plugin.WebSearch var title = keyword; string subtitle = _context.API.GetTranslation("flowlauncher_plugin_websearch_search") + " " + searchSource.Title; + //Action Keyword match apear on top + var score = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? scoreStandard : scoreStandard + 1; + if (string.IsNullOrEmpty(keyword)) { var result = new Result { Title = subtitle, SubTitle = string.Empty, - IcoPath = searchSource.IconPath + IcoPath = searchSource.IconPath, + Score = score }; results.Add(result); } @@ -65,9 +73,9 @@ namespace Flow.Launcher.Plugin.WebSearch { Title = title, SubTitle = subtitle, - Score = 6, IcoPath = searchSource.IconPath, ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword, + Score = score, Action = c => { if (_settings.OpenInNewBrowser) @@ -123,6 +131,9 @@ namespace Flow.Launcher.Plugin.WebSearch var source = _settings.SelectedSuggestion; if (source != null) { + //Suggestions appear below actual result, and appear above global action keyword match if non-global; + var score = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? scoreSuggestions : scoreSuggestions + 1; + var suggestions = await source.Suggestions(keyword, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); @@ -131,7 +142,7 @@ namespace Flow.Launcher.Plugin.WebSearch { Title = o, SubTitle = subtitle, - Score = 5, + Score = score, IcoPath = searchSource.IconPath, ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword, Action = c => From cde2224508cd3f36ce22a7beae4e63b4ba48e7b1 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 2 Mar 2021 05:54:31 +1100 Subject: [PATCH 133/398] fix missing google scholar action keyword --- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index c7c1e1591..7dd6a1290 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -2,6 +2,7 @@ "ID": "565B73353DBF4806919830B9202EE3BF", "ActionKeywords": [ "*", + "sc", "wiki", "findicon", "facebook", @@ -25,7 +26,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "1.3.5", + "Version": "1.3.6", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From db411750f9863e805787ebef2439a23c0da52744 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 4 Mar 2021 19:51:56 +1100 Subject: [PATCH 134/398] fix hibernation --- .../Images/hibernate.png | Bin 0 -> 72310 bytes Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 14 ++++++++++++-- Plugins/Flow.Launcher.Plugin.Sys/plugin.json | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Sys/Images/hibernate.png diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Images/hibernate.png b/Plugins/Flow.Launcher.Plugin.Sys/Images/hibernate.png new file mode 100644 index 0000000000000000000000000000000000000000..703204fa742d4a70af9d79552f3f064c75745417 GIT binary patch literal 72310 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 FormsApplication.SetSuspendState(PowerState.Hibernate, false, false) + IcoPath = "Images\\hibernate.png", + Action= c => + { + var info = ShellCommand.SetProcessStartInfo("shutdown", arguments:"/h"); + info.WindowStyle = ProcessWindowStyle.Hidden; + info.UseShellExecute = true; + + Process.Start(info); + + return true; + } }, new Result { diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json index 75d7073b8..5c66b73dd 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json @@ -4,7 +4,7 @@ "Name": "System Commands", "Description": "Provide System related commands. e.g. shutdown,lock,setting etc.", "Author": "qianlifeng", - "Version": "1.2.0", + "Version": "1.2.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll", From 8b59aa38dc149ae10faf5e86dfc8046a0dc02dde Mon Sep 17 00:00:00 2001 From: Jeremy Meyers Date: Thu, 4 Mar 2021 17:52:22 -0500 Subject: [PATCH 135/398] Added Plugins section with curently compatible plugins I made a table with all the plugins listed as compatible on https://github.com/Flow-Launcher/Flow.Launcher/issues/170 ... the plugin names would need to be verified in terms of being able to use them to install correctly but since there doesn't seem to be anywhere else in the docs that this lives i thought it might be a good addition. --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 73ea53135..fd6ffbf02 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,28 @@ Windows may complain about security due to code not being signed, this will be c [More tips](https://flow-launcher.github.io/docs/#/usage-tips) +## Plugins +Here are some actively maintained plugins you can install. + +| Name | Repo | Function | Woxy Port? | +| --- | --- | --- | --- | +| WoxDictionary | https://github.com/jjw24/WoxDictionary/releases/latest | Dictionary | y | +| Google Search Plus | https://github.com/jjw24/Wox.Plugin.GoogleSearch/releases/latest | Google Search | y | +| SpotifyPremium | https://github.com/jjw24/Wox.Plugin.SpotifyPremium/releases/latest | Spotify Search | y | +| Runner | https://github.com/jjw24/Wox.Plugin.Runner/releases/latest | Create command line aliases | y | +| Clipboard Manager | https://github.com/Xenolphthalein/Flow.Launcher.Plugin.ClipboardHistory | Manage your clipboard +| WindowWalker | https://github.com/taooceros/Microsoft.Plugin.WindowWalker | Alt+Tab Enhancer | Y | +| ipadr | https://github.com/taooceros/Flow.Plugin.IPAddress | Tells you your IP address | Y | +| UrlEncode | https://github.com/taooceros/Flow.Plugin.UrlEncode | Encode/Decode strings for URLs | Y | +| Putty | https://github.com/jjw24/Flow.Launcher.Plugin.Putty | Launch a PuTTy client | Y | +| FancyEmoji | https://github.com/821C3/Wox.Plugin.FancyEmoji | Emoji Search | n | +| IsPrime | https://github.com/lvonkacsoh/wox-plugin-isPrime | Tells you if an entered number is prime or not | n | +| DiskSpace | https://github.com/roose/Wox.Plugin.DiskSpace | Shows available disk space | n | +| wox-dash-velocity | https://github.com/etiago/wox-dash-velocity | Integrate with [Velocity](https://velocity.silverlakesoftware.com/) | n | +| LibreTranslate | https://github.com/taooceros/Flow.LibreTranslate | Multi-language translation tool with [LibreTranslate](https://github.com/uav4geo/LibreTranslate) back-end | y | + +There is an [open issue](https://github.com/Flow-Launcher/Flow.Launcher/issues/170) where you can request existing Wox or PowerToys Run plugins be ported. You can also do this yourself using the instructions in [our documentation](https://flow-launcher.github.io/docs/#/port-plugins). + ## Status Flow is under heavy development, but the code base is stable, so contributions are very welcome. If you would like to help maintain it, please do not hesistate to get in touch. From c082d45454f68d159ae2c84a23cb64ed98f7e6f1 Mon Sep 17 00:00:00 2001 From: Jeremy Meyers Date: Fri, 5 Mar 2021 11:51:19 -0500 Subject: [PATCH 136/398] Relocated things Moved plugins to its own file (commit needs to be accepted before the link here will work), switched Usage and Integrations --- README.md | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index fd6ffbf02..e482bd779 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,6 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up. -### Integrations - - If you use Python plugins: - - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. - - Add Python to `%PATH%` or set it in flow's settings. - - Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`. - - The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`. - - Start to launch your Python plugins. - - Flow searches files and contents via Windows Index Search, to use Everything: `pm install everything`. - ### Usage - Open flow's search window: Alt+Space is the default hotkey. - Open context menu: on the selected result, press Ctrl+O/Shift+Enter. @@ -61,27 +52,18 @@ Windows may complain about security due to code not being signed, this will be c [More tips](https://flow-launcher.github.io/docs/#/usage-tips) +### Integrations + - If you use Python plugins: + - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. + - Add Python to `%PATH%` or set it in flow's settings. + - Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`. + - The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`. + - Start to launch your Python plugins. + - Flow searches files and contents via Windows Index Search, to use Everything: `pm install everything`. + ## Plugins -Here are some actively maintained plugins you can install. -| Name | Repo | Function | Woxy Port? | -| --- | --- | --- | --- | -| WoxDictionary | https://github.com/jjw24/WoxDictionary/releases/latest | Dictionary | y | -| Google Search Plus | https://github.com/jjw24/Wox.Plugin.GoogleSearch/releases/latest | Google Search | y | -| SpotifyPremium | https://github.com/jjw24/Wox.Plugin.SpotifyPremium/releases/latest | Spotify Search | y | -| Runner | https://github.com/jjw24/Wox.Plugin.Runner/releases/latest | Create command line aliases | y | -| Clipboard Manager | https://github.com/Xenolphthalein/Flow.Launcher.Plugin.ClipboardHistory | Manage your clipboard -| WindowWalker | https://github.com/taooceros/Microsoft.Plugin.WindowWalker | Alt+Tab Enhancer | Y | -| ipadr | https://github.com/taooceros/Flow.Plugin.IPAddress | Tells you your IP address | Y | -| UrlEncode | https://github.com/taooceros/Flow.Plugin.UrlEncode | Encode/Decode strings for URLs | Y | -| Putty | https://github.com/jjw24/Flow.Launcher.Plugin.Putty | Launch a PuTTy client | Y | -| FancyEmoji | https://github.com/821C3/Wox.Plugin.FancyEmoji | Emoji Search | n | -| IsPrime | https://github.com/lvonkacsoh/wox-plugin-isPrime | Tells you if an entered number is prime or not | n | -| DiskSpace | https://github.com/roose/Wox.Plugin.DiskSpace | Shows available disk space | n | -| wox-dash-velocity | https://github.com/etiago/wox-dash-velocity | Integrate with [Velocity](https://velocity.silverlakesoftware.com/) | n | -| LibreTranslate | https://github.com/taooceros/Flow.LibreTranslate | Multi-language translation tool with [LibreTranslate](https://github.com/uav4geo/LibreTranslate) back-end | y | - -There is an [open issue](https://github.com/Flow-Launcher/Flow.Launcher/issues/170) where you can request existing Wox or PowerToys Run plugins be ported. You can also do this yourself using the instructions in [our documentation](https://flow-launcher.github.io/docs/#/port-plugins). +There is a [list of actively maintained plugins](https://github.com/Flow-Launcher/docs/Plugins.md) in the documentation section, some of which are integrated from other launchers. ## Status From 99ca4093f285b15f0efc422b2295ee02beb52f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 6 Mar 2021 13:30:18 +0800 Subject: [PATCH 137/398] Add Open Log Location command to sys plugin --- Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index ca971b3f9..8828f3cef 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Forms; using System.Windows.Interop; using Flow.Launcher.Infrastructure; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; using Application = System.Windows.Application; using Control = System.Windows.Controls.Control; @@ -267,6 +269,18 @@ namespace Flow.Launcher.Plugin.Sys context.API.CheckForNewUpdate(); return true; } + }, + new Result + { + Title = "Open Log Location", + SubTitle = "Open Log Position", + IcoPath = "Images\\app.png", + Action = c => + { + var logPath = Path.Combine(DataLocation.DataDirectory(), "Logs", Constant.Version); + Process.Start("explorer".SetProcessStartInfo(arguments: logPath)); + return true; + } } }); return results; From f7ded13c0a01a48af317fd5568db3aecb3828456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Sat, 6 Mar 2021 18:58:19 +0800 Subject: [PATCH 138/398] Version Bump --- Plugins/Flow.Launcher.Plugin.Sys/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json index 5c66b73dd..844bcfb53 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json @@ -4,7 +4,7 @@ "Name": "System Commands", "Description": "Provide System related commands. e.g. shutdown,lock,setting etc.", "Author": "qianlifeng", - "Version": "1.2.1", + "Version": "1.2.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll", From da3f20a61a07e4ac4fcce88ea72fb3e07795f4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 8 Mar 2021 16:16:54 +0800 Subject: [PATCH 139/398] Stop await indexing when indexing is required and cache exist --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 44 +++++++++---------- .../Flow.Launcher.Plugin.Program/plugin.json | 2 +- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index bd09cd9c6..d0347fa67 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -85,20 +85,20 @@ namespace Flow.Launcher.Plugin.Program { _context = context; - await Task.Run(() => - { - _settings = _settingsStorage.Load(); + _settings = _settingsStorage.Load(); - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", () => - { - _win32Storage = new BinaryStorage("Win32"); - _win32s = _win32Storage.TryLoad(new Win32[] { }); - _uwpStorage = new BinaryStorage("UWP"); - _uwps = _uwpStorage.TryLoad(new UWP.Application[] { }); - }); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); + await Task.Yield(); + + Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", () => + { + _win32Storage = new BinaryStorage("Win32"); + _win32s = _win32Storage.TryLoad(new Win32[] { }); + _uwpStorage = new BinaryStorage("UWP"); + _uwps = _uwpStorage.TryLoad(new UWP.Application[] { }); }); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); + Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); + bool indexedWinApps = false; bool indexedUWPApps = false; @@ -121,16 +121,19 @@ namespace Flow.Launcher.Plugin.Program } }); - await Task.WhenAll(a, b); - - if (indexedWinApps && indexedUWPApps) - _settings.LastIndexTime = DateTime.Today; + var indexTask = Task.WhenAll(a, b).ContinueWith(t => + { + if (indexedWinApps && indexedUWPApps) + _settings.LastIndexTime = DateTime.Today; + }); + + if (!(_win32s.Any() && _uwps.Any())) + await indexTask; } public static void IndexWin32Programs() { var win32S = Win32.All(_settings); - _win32s = win32S; } @@ -138,20 +141,15 @@ namespace Flow.Launcher.Plugin.Program { var windows10 = new Version(10, 0); var support = Environment.OSVersion.Version.Major >= windows10.Major; - var applications = support ? UWP.All() : new UWP.Application[] { }; - _uwps = applications; } public static async Task IndexPrograms() { var t1 = Task.Run(IndexWin32Programs); - var t2 = Task.Run(IndexUwpPrograms); - await Task.WhenAll(t1, t2).ConfigureAwait(false); - _settings.LastIndexTime = DateTime.Today; } @@ -245,4 +243,4 @@ namespace Flow.Launcher.Plugin.Program await IndexPrograms(); } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index 082f0d853..48386118a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.4.3", + "Version": "1.4.4", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", From 17a04d65c19dbf4f4215da68dd25196270876be5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 9 Mar 2021 20:59:25 +1100 Subject: [PATCH 140/398] fix private variable naming convention --- Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 3aa087821..4e0f3c80a 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -21,7 +21,7 @@ namespace Flow.Launcher.Plugin.Shell public class Main : IPlugin, ISettingProvider, IPluginI18n, IContextMenu, ISavable { private const string Image = "Images/shell.png"; - private PluginInitContext _context; + private PluginInitContext context; private bool _winRStroked; private readonly KeyboardSimulator _keyboardSimulator = new KeyboardSimulator(new InputSimulator()); @@ -108,14 +108,14 @@ namespace Flow.Launcher.Plugin.Shell { if (m.Key == cmd) { - result.SubTitle = string.Format(_context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value); + result.SubTitle = string.Format(context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value); return null; } var ret = new Result { Title = m.Key, - SubTitle = string.Format(_context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), + SubTitle = string.Format(context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), IcoPath = Image, Action = c => { @@ -134,7 +134,7 @@ namespace Flow.Launcher.Plugin.Shell { Title = cmd, Score = 5000, - SubTitle = _context.API.GetTranslation("flowlauncher_plugin_cmd_execute_through_shell"), + SubTitle = context.API.GetTranslation("flowlauncher_plugin_cmd_execute_through_shell"), IcoPath = Image, Action = c => { @@ -152,7 +152,7 @@ namespace Flow.Launcher.Plugin.Shell .Select(m => new Result { Title = m.Key, - SubTitle = string.Format(_context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), + SubTitle = string.Format(context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), IcoPath = Image, Action = c => { @@ -234,13 +234,13 @@ namespace Flow.Launcher.Plugin.Shell { var name = "Plugin: Shell"; var message = $"Command not found: {e.Message}"; - _context.API.ShowMsg(name, message); + context.API.ShowMsg(name, message); } catch(Win32Exception e) { var name = "Plugin: Shell"; var message = $"Error running the command: {e.Message}"; - _context.API.ShowMsg(name, message); + context.API.ShowMsg(name, message); } } @@ -275,7 +275,7 @@ namespace Flow.Launcher.Plugin.Shell public void Init(PluginInitContext context) { - this._context = context; + this.context = context; context.API.GlobalKeyboardEvent += API_GlobalKeyboardEvent; } @@ -301,7 +301,7 @@ namespace Flow.Launcher.Plugin.Shell private void OnWinRPressed() { - _context.API.ChangeQuery($"{_context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeperater}"); + context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeperater}"); Application.Current.MainWindow.Visibility = Visibility.Visible; } @@ -312,12 +312,12 @@ namespace Flow.Launcher.Plugin.Shell public string GetTranslatedPluginTitle() { - return _context.API.GetTranslation("flowlauncher_plugin_cmd_plugin_name"); + return context.API.GetTranslation("flowlauncher_plugin_cmd_plugin_name"); } public string GetTranslatedPluginDescription() { - return _context.API.GetTranslation("flowlauncher_plugin_cmd_plugin_description"); + return context.API.GetTranslation("flowlauncher_plugin_cmd_plugin_description"); } public List LoadContextMenus(Result selectedResult) @@ -326,7 +326,7 @@ namespace Flow.Launcher.Plugin.Shell { new Result { - Title = _context.API.GetTranslation("flowlauncher_plugin_cmd_run_as_different_user"), + Title = context.API.GetTranslation("flowlauncher_plugin_cmd_run_as_different_user"), Action = c => { Task.Run(() =>Execute(ShellCommand.RunAsDifferentUser, PrepareProcessStartInfo(selectedResult.Title))); @@ -336,7 +336,7 @@ namespace Flow.Launcher.Plugin.Shell }, new Result { - Title = _context.API.GetTranslation("flowlauncher_plugin_cmd_run_as_administrator"), + Title = context.API.GetTranslation("flowlauncher_plugin_cmd_run_as_administrator"), Action = c => { Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true)); From 22617aef33e216a69500eb2e77bff3849246cabf Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 9 Mar 2021 21:04:28 +1100 Subject: [PATCH 141/398] add copy cmd to context menu --- .../Flow.Launcher.Plugin.Shell/Images/copy.png | Bin 0 -> 39761 bytes .../Languages/en.xaml | 1 + Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 10 ++++++++++ 3 files changed, 11 insertions(+) create mode 100644 Plugins/Flow.Launcher.Plugin.Shell/Images/copy.png diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Images/copy.png b/Plugins/Flow.Launcher.Plugin.Shell/Images/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..cc3bf4ea8281e9ae520005dbcfd424012da3baf0 GIT binary patch literal 39761 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 literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml index 586ac293b..8665e17bb 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Shell/Languages/en.xaml @@ -11,4 +11,5 @@ this command has been executed {0} times execute command through command shell Run As Administrator + Copy the command \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 4e0f3c80a..c442505af 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -343,6 +343,16 @@ namespace Flow.Launcher.Plugin.Shell return true; }, IcoPath = Image + }, + new Result + { + Title = context.API.GetTranslation("flowlauncher_plugin_cmd_copy"), + Action = c => + { + Clipboard.SetDataObject(selectedResult.Title); + return true; + }, + IcoPath = "Images/copy.png" } }; From bd921f8d0cdcb0a270619fa27f3ddef5afcca5fc Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 9 Mar 2021 21:04:57 +1100 Subject: [PATCH 142/398] add context menu run as admin icon --- .../Flow.Launcher.Plugin.Shell/Images/admin.png | Bin 0 -> 65703 bytes Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Shell/Images/admin.png diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Images/admin.png b/Plugins/Flow.Launcher.Plugin.Shell/Images/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..bc3fe877861b6b2ee15e9d1969a609f9fc24dde9 GIT binary patch literal 65703 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 z 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 z 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 Oyp))nXqm8{_x}g7>_LVA literal 0 HcmV?d00001 diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index c442505af..bd316a6bf 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -342,7 +342,7 @@ namespace Flow.Launcher.Plugin.Shell Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true)); return true; }, - IcoPath = Image + IcoPath = "Images/admin.png" }, new Result { From e312fefbb1798803a301d9ab1c33116a8a4cba1e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 9 Mar 2021 21:05:38 +1100 Subject: [PATCH 143/398] verison bump Shell plugin --- Plugins/Flow.Launcher.Plugin.Shell/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json index 4ad572cf6..b38db70ed 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json @@ -4,7 +4,7 @@ "Name": "Shell", "Description": "Provide executing commands from Flow Launcher. Commands should start with >", "Author": "qianlifeng", - "Version": "1.1.2", + "Version": "1.2.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Shell.dll", From 43f1cae35020de48a7b46f2e13a916c52462327d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Mar 2021 06:01:29 +1100 Subject: [PATCH 144/398] fix exclusion of Windows Index file or folder search --- .../Search/SearchManager.cs | 25 ++++++++++++++++++- .../Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index d5f882d5c..15ffa19c4 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -67,7 +67,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { results.UnionWith(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); - return results.ToList(); + return FilterOutResultsFromWindowsIndexExclusionList(results).ToList(); } var locationPath = querySearch; @@ -159,6 +159,29 @@ namespace Flow.Launcher.Plugin.Explorer.Search token).ConfigureAwait(false); } + private HashSet FilterOutResultsFromWindowsIndexExclusionList(HashSet results) + { + var indexExclusionList = settings.IndexSearchExcludedSubdirectoryPaths; + + var indexExclusionListCount = indexExclusionList.Count; + + if (indexExclusionListCount == 0) + return results; + + var filteredResults = new HashSet(PathEqualityComparator.Instance); + + foreach (var r in results) + { + for (var i = 0; i < indexExclusionListCount; i++) + { + if (!r.SubTitle.StartsWith(indexExclusionList[0].Path, StringComparison.OrdinalIgnoreCase)) + filteredResults.Add(r); + } + } + + return filteredResults; + } + private bool UseWindowsIndexForDirectorySearch(string locationPath) { var pathToDirectory = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 63ca66a1e..990ceadda 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.7.1", + "Version": "1.7.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", From a5e3049adcfc71f1f39ce56375e881839b406c34 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Mar 2021 07:39:14 +1100 Subject: [PATCH 145/398] fix typo --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 15ffa19c4..f719a22d9 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -174,7 +174,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search { for (var i = 0; i < indexExclusionListCount; i++) { - if (!r.SubTitle.StartsWith(indexExclusionList[0].Path, StringComparison.OrdinalIgnoreCase)) + if (!r.SubTitle.StartsWith(indexExclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) filteredResults.Add(r); } } From b642898132095b4cc3294a82eaefb42127ac41cb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Mar 2021 08:12:03 +1100 Subject: [PATCH 146/398] update loop logic --- .../Search/SearchManager.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index f719a22d9..2983c7d35 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -172,11 +172,19 @@ namespace Flow.Launcher.Plugin.Explorer.Search foreach (var r in results) { + var excludeResult = false; + for (var i = 0; i < indexExclusionListCount; i++) { - if (!r.SubTitle.StartsWith(indexExclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) - filteredResults.Add(r); + if (r.SubTitle.StartsWith(indexExclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) + { + excludeResult = true; + break; + } } + + if (!excludeResult) + filteredResults.Add(r); } return filteredResults; From b2dc2ef71b768e402ab685e8883dccdd8b6c243b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 16 Mar 2021 07:12:01 +1100 Subject: [PATCH 147/398] do not exclude if in Quick Access list --- .../Search/SearchManager.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 2983c7d35..e78324718 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -65,9 +65,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!querySearch.IsLocationPathString() && !isEnvironmentVariablePath) { - results.UnionWith(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); + var filteredResults = FilterOutResultsFromWindowsIndexExclusionList( + await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); - return FilterOutResultsFromWindowsIndexExclusionList(results).ToList(); + results.UnionWith(filteredResults); + + return results.ToList(); } var locationPath = querySearch; From e73b2a05ce14acc3513a6293581655322599892f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 16 Mar 2021 07:12:36 +1100 Subject: [PATCH 148/398] change to for loop --- .../Search/SearchManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index e78324718..a1fc6f6ed 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -1,4 +1,4 @@ -using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; +using Flow.Launcher.Plugin.Explorer.Search.DirectoryInfo; using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex; using Flow.Launcher.Plugin.SharedCommands; @@ -162,24 +162,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search token).ConfigureAwait(false); } - private HashSet FilterOutResultsFromWindowsIndexExclusionList(HashSet results) + private HashSet FilterOutResultsFromWindowsIndexExclusionList(List results) { var indexExclusionList = settings.IndexSearchExcludedSubdirectoryPaths; var indexExclusionListCount = indexExclusionList.Count; if (indexExclusionListCount == 0) - return results; + return results.ToHashSet(); var filteredResults = new HashSet(PathEqualityComparator.Instance); - foreach (var r in results) + for (var index = 0; index < results.Count; index++) { var excludeResult = false; for (var i = 0; i < indexExclusionListCount; i++) { - if (r.SubTitle.StartsWith(indexExclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) + if (results[index].SubTitle.StartsWith(indexExclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) { excludeResult = true; break; @@ -187,7 +187,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search } if (!excludeResult) - filteredResults.Add(r); + filteredResults.Add(results[index]); } return filteredResults; From 7aa1396821f6951ae365d4cc53790d938918e174 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 16 Mar 2021 21:33:40 +1100 Subject: [PATCH 149/398] move index exclude method to IndexSearch class and add async --- .../Search/SearchManager.cs | 39 ++---------------- .../Search/WindowsIndex/IndexSearch.cs | 40 ++++++++++++++++++- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index a1fc6f6ed..e9b4b0fc1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -65,10 +65,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!querySearch.IsLocationPathString() && !isEnvironmentVariablePath) { - var filteredResults = FilterOutResultsFromWindowsIndexExclusionList( - await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); - - results.UnionWith(filteredResults); + results.UnionWith(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); return results.ToList(); } @@ -112,6 +109,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return await IndexSearch.WindowsIndexSearchAsync(querySearchString, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForFileContentSearch, + settings.IndexSearchExcludedSubdirectoryPaths, query, token).ConfigureAwait(false); } @@ -147,6 +145,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search return await IndexSearch.WindowsIndexSearchAsync(querySearchString, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForAllFilesAndFolders, + settings.IndexSearchExcludedSubdirectoryPaths, query, token).ConfigureAwait(false); } @@ -158,41 +157,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search return await IndexSearch.WindowsIndexSearchAsync(path, queryConstructor.CreateQueryHelper().ConnectionString, queryConstructor.QueryForTopLevelDirectorySearch, + settings.IndexSearchExcludedSubdirectoryPaths, query, token).ConfigureAwait(false); } - private HashSet FilterOutResultsFromWindowsIndexExclusionList(List results) - { - var indexExclusionList = settings.IndexSearchExcludedSubdirectoryPaths; - - var indexExclusionListCount = indexExclusionList.Count; - - if (indexExclusionListCount == 0) - return results.ToHashSet(); - - var filteredResults = new HashSet(PathEqualityComparator.Instance); - - for (var index = 0; index < results.Count; index++) - { - var excludeResult = false; - - for (var i = 0; i < indexExclusionListCount; i++) - { - if (results[index].SubTitle.StartsWith(indexExclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) - { - excludeResult = true; - break; - } - } - - if (!excludeResult) - filteredResults.Add(results[index]); - } - - return filteredResults; - } - private bool UseWindowsIndexForDirectorySearch(string locationPath) { var pathToDirectory = FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index b1e1d7622..ecadd61ec 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -1,4 +1,5 @@ using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; using Microsoft.Search.Interop; using System; using System.Collections.Generic; @@ -83,7 +84,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex } internal async static Task> WindowsIndexSearchAsync(string searchString, string connectionString, - Func constructQuery, Query query, + Func constructQuery, + List exclusionList, + Query query, CancellationToken token) { var regexMatch = Regex.Match(searchString, reservedStringPattern); @@ -92,8 +95,41 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return new List(); var constructedQuery = constructQuery(searchString); - return await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token); + return await RemoveResultsInExclusionList( + await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token), + exclusionList); + } + private async static Task> RemoveResultsInExclusionList(List results, List exclusionList) + { + var indexExclusionListCount = exclusionList.Count; + + if (indexExclusionListCount == 0) + return results; + + var filteredResults = new List(); + + await Task.Run(() => + { + for (var index = 0; index < results.Count; index++) + { + var excludeResult = false; + + for (var i = 0; i < indexExclusionListCount; i++) + { + if (results[index].SubTitle.StartsWith(exclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) + { + excludeResult = true; + break; + } + } + + if (!excludeResult) + filteredResults.Add(results[index]); + } + }); + + return filteredResults; } internal static bool PathIsIndexed(string path) From b138838805e9542d7d362d6601be34993bbcce95 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 17 Mar 2021 06:32:53 +1100 Subject: [PATCH 150/398] add ConfigureWait and cancellation token --- .../Search/WindowsIndex/IndexSearch.cs | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index ecadd61ec..559ef4647 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -96,11 +96,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex var constructedQuery = constructQuery(searchString); return await RemoveResultsInExclusionList( - await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token), - exclusionList); + await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token).ConfigureAwait(false), + exclusionList, + token).ConfigureAwait(false); } - private async static Task> RemoveResultsInExclusionList(List results, List exclusionList) + private async static Task> RemoveResultsInExclusionList(List results, List exclusionList, CancellationToken token) { var indexExclusionListCount = exclusionList.Count; @@ -109,27 +110,38 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex var filteredResults = new List(); - await Task.Run(() => + try { - for (var index = 0; index < results.Count; index++) + await Task.Run(() => { - var excludeResult = false; - - for (var i = 0; i < indexExclusionListCount; i++) + for (var index = 0; index < results.Count; index++) { - if (results[index].SubTitle.StartsWith(exclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) + token.ThrowIfCancellationRequested(); + + var excludeResult = false; + + for (var i = 0; i < indexExclusionListCount; i++) { - excludeResult = true; - break; + token.ThrowIfCancellationRequested(); + + if (results[index].SubTitle.StartsWith(exclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) + { + excludeResult = true; + break; + } } + + if (!excludeResult) + filteredResults.Add(results[index]); } + }, token); - if (!excludeResult) - filteredResults.Add(results[index]); - } - }); - - return filteredResults; + return filteredResults; + } + catch (OperationCanceledException) + { + return new List(); + } } internal static bool PathIsIndexed(string path) From f4971e2e96ad0b59ced9dd79fd9718e0c1ec0b5b Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 18 Mar 2021 06:44:00 +1100 Subject: [PATCH 151/398] remove async and task run --- .../Search/WindowsIndex/IndexSearch.cs | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 559ef4647..174bdcfae 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -95,13 +95,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex return new List(); var constructedQuery = constructQuery(searchString); - return await RemoveResultsInExclusionList( + return RemoveResultsInExclusionList( await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token).ConfigureAwait(false), exclusionList, - token).ConfigureAwait(false); + token); } - private async static Task> RemoveResultsInExclusionList(List results, List exclusionList, CancellationToken token) + private static List RemoveResultsInExclusionList(List results, List exclusionList, CancellationToken token) { var indexExclusionListCount = exclusionList.Count; @@ -110,38 +110,28 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex var filteredResults = new List(); - try + for (var index = 0; index < results.Count; index++) { - await Task.Run(() => + token.ThrowIfCancellationRequested(); + + var excludeResult = false; + + for (var i = 0; i < indexExclusionListCount; i++) { - for (var index = 0; index < results.Count; index++) + token.ThrowIfCancellationRequested(); + + if (results[index].SubTitle.StartsWith(exclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) { - token.ThrowIfCancellationRequested(); - - var excludeResult = false; - - for (var i = 0; i < indexExclusionListCount; i++) - { - token.ThrowIfCancellationRequested(); - - if (results[index].SubTitle.StartsWith(exclusionList[i].Path, StringComparison.OrdinalIgnoreCase)) - { - excludeResult = true; - break; - } - } - - if (!excludeResult) - filteredResults.Add(results[index]); + excludeResult = true; + break; } - }, token); + } - return filteredResults; - } - catch (OperationCanceledException) - { - return new List(); + if (!excludeResult) + filteredResults.Add(results[index]); } + + return filteredResults; } internal static bool PathIsIndexed(string path) From 947992353145a5ba3ce5f08549d7877b017b3580 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 18 Mar 2021 06:50:55 +1100 Subject: [PATCH 152/398] remove unnecessary catching OperationCanceledException --- .../Search/WindowsIndex/IndexSearch.cs | 4 -- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 38 ++++++------------- .../SuggestionSources/Baidu.cs | 5 --- .../SuggestionSources/Bing.cs | 4 -- .../SuggestionSources/Google.cs | 4 -- 5 files changed, 12 insertions(+), 43 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 174bdcfae..22d89ba8f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -63,10 +63,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex } } } - catch (OperationCanceledException) - { - return new List(); // The source code indicates that without adding members, it won't allocate an array - } catch (InvalidOperationException e) { // Internal error from ExecuteReader(): Connection closed. diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index d0347fa67..0f6968092 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -50,35 +50,21 @@ namespace Flow.Launcher.Plugin.Program win32 = _win32s; uwps = _uwps; - try + var result = await Task.Run(delegate { - var result = await Task.Run(delegate - { - try - { - return win32.Cast() - .Concat(uwps) - .AsParallel() - .WithCancellation(token) - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)) - .Where(r => r?.Score > 0) - .ToList(); - } - catch (OperationCanceledException) - { - return null; - } - }, token).ConfigureAwait(false); + return win32.Cast() + .Concat(uwps) + .AsParallel() + .WithCancellation(token) + .Where(p => p.Enabled) + .Select(p => p.Result(query.Search, _context.API)) + .Where(r => r?.Score > 0) + .ToList(); + }, token).ConfigureAwait(false); - token.ThrowIfCancellationRequested(); + token.ThrowIfCancellationRequested(); - return result; - } - catch (OperationCanceledException) - { - return null; - } + return result; } public async Task InitAsync(PluginInitContext context) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index 94458ff52..ccb5b20d7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -30,11 +30,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); return null; } - catch (OperationCanceledException) - { - return null; - } - if (string.IsNullOrEmpty(result)) return new List(); Match match = _reg.Match(result); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 966a6ca16..38c5fb4a0 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -45,10 +45,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); return null; } - catch (OperationCanceledException) - { - return new List(); - } catch (JsonException e) { Log.Exception("|Bing.Suggestions|can't parse suggestions", e); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 91ab921b6..c5f43d081 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -37,10 +37,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); return null; } - catch (OperationCanceledException) - { - return new List(); - } catch (JsonException e) { Log.Exception("|Google.Suggestions|can't parse suggestions", e); From 541e91df605496edf7ae041ca1eb90f241dfecb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 18 Mar 2021 11:38:07 +0800 Subject: [PATCH 153/398] Move initialization of BrowserBookmark to Init() --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index b889bb0d0..df87d14e7 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -23,15 +23,17 @@ namespace Flow.Launcher.Plugin.BrowserBookmark public Main() { - _storage = new PluginJsonStorage(); - _settings = _storage.Load(); - - cachedBookmarks = Bookmarks.LoadAllBookmarks(); + } public void Init(PluginInitContext context) { this.context = context; + + _storage = new PluginJsonStorage(); + _settings = _storage.Load(); + + cachedBookmarks = Bookmarks.LoadAllBookmarks(); } public List Query(Query query) From 409ccae7f20ddeceaaedad65c5110460c72b7ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 18 Mar 2021 11:39:13 +0800 Subject: [PATCH 154/398] Move initialization of Calculator to Init() --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index ab8ce892a..7ee69494a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -32,13 +32,7 @@ namespace Flow.Launcher.Plugin.Caculator static Main() { - MagesEngine = new Engine(new Configuration - { - Scope = new Dictionary - { - { "e", Math.E }, // e is not contained in the default mages engine - } - }); + } public void Init(PluginInitContext context) @@ -47,6 +41,14 @@ namespace Flow.Launcher.Plugin.Caculator _viewModel = new SettingsViewModel(); _settings = _viewModel.Settings; + + MagesEngine = new Engine(new Configuration + { + Scope = new Dictionary + { + { "e", Math.E }, // e is not contained in the default mages engine + } + }); } public List Query(Query query) From 6430e2562d974cbab9677ebd38e4ca345f937535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 18 Mar 2021 11:43:41 +0800 Subject: [PATCH 155/398] Use EnumerateDirectories instead of GetDirectories --- Flow.Launcher.Core/Plugin/PluginConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index 46f79c60c..ea2119e60 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -20,7 +20,7 @@ namespace Flow.Launcher.Core.Plugin public static List Parse(string[] pluginDirectories) { var allPluginMetadata = new List(); - var directories = pluginDirectories.SelectMany(Directory.GetDirectories); + var directories = pluginDirectories.SelectMany(Directory.EnumerateDirectories); // todo use linq when diable plugin is implmented since parallel.foreach + list is not thread saft foreach (var directory in directories) From 8acde564554aa7322dc1359712fba9bbaa9d0162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 18 Mar 2021 11:59:48 +0800 Subject: [PATCH 156/398] remove readonly field to allow init in Init() --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index df87d14e7..5bff0e182 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -18,13 +18,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark private List cachedBookmarks = new List(); - private readonly Settings _settings; - private readonly PluginJsonStorage _storage; - - public Main() - { - - } + private Settings _settings { get; set;} + private PluginJsonStorage _storage { get; set;} public void Init(PluginInitContext context) { From 9182fdb2bd286691fac9c519e460186bd533b0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 20 Mar 2021 18:02:35 +0800 Subject: [PATCH 157/398] Remove readonly in Calculator --- Plugins/Flow.Launcher.Plugin.Calculator/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index 7ee69494a..3c60bbf5a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -24,7 +24,7 @@ namespace Flow.Launcher.Plugin.Caculator @"[ei]|[0-9]|[\+\-\*\/\^\., ""]|[\(\)\|\!\[\]]" + @")+$", RegexOptions.Compiled); private static readonly Regex RegBrackets = new Regex(@"[\(\)\[\]]", RegexOptions.Compiled); - private static readonly Engine MagesEngine; + private static Engine MagesEngine; private PluginInitContext Context { get; set; } private static Settings _settings; From 480887edaf341061b2d031739a8895fb5278fd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 20 Mar 2021 18:03:11 +0800 Subject: [PATCH 158/398] Version Bump --- Plugins/Flow.Launcher.Plugin.Calculator/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json index 9bf532e87..d37a1d282 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json @@ -4,7 +4,7 @@ "Name": "Calculator", "Description": "Provide mathematical calculations.(Try 5*3-2 in Flow Launcher)", "Author": "cxfksword", - "Version": "1.1.5", + "Version": "1.1.6", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll", From c8c28db624491cb5e64a5cf954e19ddc110e3a3a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Mar 2021 05:01:45 +1100 Subject: [PATCH 159/398] version bump BrowserBookmarks plugin --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index 62311f514..e3ef792ec 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -4,7 +4,7 @@ "Name": "Browser Bookmarks", "Description": "Search your browser bookmarks", "Author": "qianlifeng, Ioannis G.", - "Version": "1.4.0", + "Version": "1.4.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll", From ab7d00eacc9a7e18014e03999db0968567471d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 23 Mar 2021 17:25:46 +0800 Subject: [PATCH 160/398] Make IPlugin inherit from IAsyncPlugin --- .../Plugin/PluginAssemblyLoader.cs | 4 +-- Flow.Launcher.Core/Plugin/PluginManager.cs | 31 +++++-------------- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 24 +++++++------- Flow.Launcher.Plugin/IAsyncPlugin.cs | 2 +- Flow.Launcher.Plugin/IPlugin.cs | 8 ++++- Flow.Launcher.Plugin/PluginPair.cs | 2 +- 6 files changed, 30 insertions(+), 41 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs index f473cbc65..e18985fe7 100644 --- a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -49,10 +49,10 @@ namespace Flow.Launcher.Core.Plugin return LoadFromAssemblyPath(assemblyPath); } - internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, params Type[] types) + internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type) { var allTypes = assembly.ExportedTypes; - return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Intersect(types).Any()); + return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Any(t => t == type)); } internal bool ExistsInReferencedPackage(AssemblyName assemblyName) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 66eb0f0ec..8c9fd2eb9 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -97,16 +97,9 @@ namespace Flow.Launcher.Core.Plugin { try { - var milliseconds = pair.Plugin switch - { - IAsyncPlugin plugin - => await Stopwatch.DebugAsync($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", - () => plugin.InitAsync(new PluginInitContext(pair.Metadata, API))), - IPlugin plugin - => Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", - () => plugin.Init(new PluginInitContext(pair.Metadata, API))), - _ => throw new ArgumentException(), - }; + var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", + () => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API))); + pair.Metadata.InitTime += milliseconds; Log.Info( $"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); @@ -169,19 +162,9 @@ namespace Flow.Launcher.Core.Plugin long milliseconds = -1L; - switch (pair.Plugin) - { - case IAsyncPlugin plugin: - milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", - async () => results = await plugin.QueryAsync(query, token).ConfigureAwait(false)); - break; - case IPlugin plugin: - await Task.Run(() => milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", - () => results = plugin.Query(query)), token).ConfigureAwait(false); - break; - default: - throw new ArgumentOutOfRangeException(); - } + milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", + async () => results = await pair.Plugin.QueryAsync(query, token).ConfigureAwait(false)); + token.ThrowIfCancellationRequested(); if (results == null) return results; @@ -199,7 +182,7 @@ namespace Flow.Launcher.Core.Plugin } catch (Exception e) { - Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); + Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); } return results; diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 2c75f9633..92aca8109 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -44,23 +44,23 @@ namespace Flow.Launcher.Core.Plugin #if DEBUG var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); var assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin), + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IAsyncPlugin)); - var plugin = Activator.CreateInstance(type); + var plugin = Activator.CreateInstance(type) as IAsyncPlugin; #else Assembly assembly = null; - object plugin = null; + IAsyncPlugin plugin = null; try { var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin), + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IAsyncPlugin)); - plugin = Activator.CreateInstance(type); + plugin = Activator.CreateInstance(type) as IAsyncPlugin; } catch (Exception e) when (assembly == null) { @@ -78,13 +78,13 @@ namespace Flow.Launcher.Core.Plugin { Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e); } - +#endif if (plugin == null) { erroredPlugins.Add(metadata.Name); return; } -#endif + plugins.Add(new PluginPair { Plugin = plugin, @@ -139,7 +139,7 @@ namespace Flow.Launcher.Core.Plugin } else { - Log.Error("PluginsLoader","Failed to set Python path despite the environment variable PATH is found", "PythonPlugins"); + Log.Error("PluginsLoader", "Failed to set Python path despite the environment variable PATH is found", "PythonPlugins"); } } } @@ -152,7 +152,7 @@ namespace Flow.Launcher.Core.Plugin } else { - Log.Error("PluginsLoader",$"Tried to automatically set from Settings.PythonDirectory " + + Log.Error("PluginsLoader", $"Tried to automatically set from Settings.PythonDirectory " + $"but can't find python executable in {path}", "PythonPlugins"); } } @@ -205,7 +205,7 @@ namespace Flow.Launcher.Core.Plugin } else { - Log.Error("PluginsLoader", + Log.Error("PluginsLoader", $"Failed to set Python path after Droplex install, {pythonPath} does not exist", "PythonPlugins"); } @@ -215,8 +215,8 @@ namespace Flow.Launcher.Core.Plugin if (string.IsNullOrEmpty(settings.PythonDirectory)) { MessageBox.Show("Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom)."); - Log.Error("PluginsLoader", - $"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.", + Log.Error("PluginsLoader", + $"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.", "PythonPlugins"); return new List(); diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index b0b41cc22..3dcb97d99 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -19,7 +19,7 @@ namespace Flow.Launcher.Plugin /// Query to search /// Cancel when querying job is obsolete /// - Task> QueryAsync(Query query, CancellationToken token); + abstract Task> QueryAsync(Query query, CancellationToken token); ///

/// Initialize plugin asynchrously (will still wait finish to continue) diff --git a/Flow.Launcher.Plugin/IPlugin.cs b/Flow.Launcher.Plugin/IPlugin.cs index 203dc9af7..bac93d090 100644 --- a/Flow.Launcher.Plugin/IPlugin.cs +++ b/Flow.Launcher.Plugin/IPlugin.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; namespace Flow.Launcher.Plugin { @@ -9,7 +11,7 @@ namespace Flow.Launcher.Plugin /// or performaing CPU intense jobs (performing better with cancellation), please try the IAsyncPlugin interface /// /// - public interface IPlugin + public interface IPlugin : IAsyncPlugin { /// /// Querying when user's search changes @@ -27,5 +29,9 @@ namespace Flow.Launcher.Plugin /// /// void Init(PluginInitContext context); + + Task IAsyncPlugin.InitAsync(PluginInitContext context) => Task.Run(() => Init(context)); + + Task> IAsyncPlugin.QueryAsync(Query query, CancellationToken token) => Task.Run(() => Query(query)); } } diff --git a/Flow.Launcher.Plugin/PluginPair.cs b/Flow.Launcher.Plugin/PluginPair.cs index e8954b7a0..7bf634691 100644 --- a/Flow.Launcher.Plugin/PluginPair.cs +++ b/Flow.Launcher.Plugin/PluginPair.cs @@ -2,7 +2,7 @@ { public class PluginPair { - public object Plugin { get; internal set; } + public IAsyncPlugin Plugin { get; internal set; } public PluginMetadata Metadata { get; internal set; } From 6bbedbaaa16eb00d93f74783922edc045c40882a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 23 Mar 2021 17:55:40 +0800 Subject: [PATCH 161/398] remove the explicitly abstract --- Flow.Launcher.Plugin/IAsyncPlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin/IAsyncPlugin.cs b/Flow.Launcher.Plugin/IAsyncPlugin.cs index 3dcb97d99..b0b41cc22 100644 --- a/Flow.Launcher.Plugin/IAsyncPlugin.cs +++ b/Flow.Launcher.Plugin/IAsyncPlugin.cs @@ -19,7 +19,7 @@ namespace Flow.Launcher.Plugin /// Query to search /// Cancel when querying job is obsolete /// - abstract Task> QueryAsync(Query query, CancellationToken token); + Task> QueryAsync(Query query, CancellationToken token); /// /// Initialize plugin asynchrously (will still wait finish to continue) From 6fe83d84c5343703bc94b2fa9f0a6301cf744b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sat, 27 Mar 2021 17:26:53 +0800 Subject: [PATCH 162/398] make JsonStorge used with single instance --- Flow.Launcher.Plugin/IPublicAPI.cs | 20 ++++++++++++++-- Flow.Launcher/PublicAPIInstance.cs | 37 ++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index 07e2b2700..0c1cd7bfd 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -165,10 +165,26 @@ namespace Flow.Launcher.Plugin T LoadJsonStorage() where T : new(); /// - /// Save JsonStorage for current plugin. This is the method used to save settings to json in Flow + /// Save JsonStorage for current plugin. This is the method used to save settings to json in Flow.Launcher + /// This method will save the original instance loaded with LoadJsonStorage. /// /// Type for Serialization /// - void SaveJsonStorage(T setting) where T : new(); + void SaveJsonStorage() where T : new(); + + /// + /// Save JsonStorage for current plugin. This is the method used to save settings to json in Flow.Launcher + /// This method will override the original class instance loaded from LoadJsonStorage + /// + /// Type for Serialization + /// + void SaveJsonStorage(T settings) where T : new(); + + /// + /// Backup the JsonStorage you loaded from LoadJsonStorage + /// + /// + /// + void BackupJsonStorage() where T : new(); } } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 9b41c2c05..77902ecd9 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -131,9 +131,42 @@ namespace Flow.Launcher public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName); - public T LoadJsonStorage() where T : new() => new PluginJsonStorage().Load(); + private readonly Dictionary PluginJsonStorages = new Dictionary(); - public void SaveJsonStorage(T setting) where T : new() => new PluginJsonStorage(setting).Save(); + public T LoadJsonStorage() where T : new() + { + var type = typeof(T); + if (!PluginJsonStorages.ContainsKey(type)) + PluginJsonStorages[type] = new PluginJsonStorage(); + + return PluginJsonStorages[type].Load(); + } + + public void SaveJsonStorage() where T : new() + { + var type = typeof(T); + if (!PluginJsonStorages.ContainsKey(type)) + PluginJsonStorages[type] = new PluginJsonStorage(); + + PluginJsonStorages[type].Save(); + } + + public void SaveJsonStorage(T settings) where T : new() + { + var type = typeof(T); + PluginJsonStorages[type] = new PluginJsonStorage(settings); + + PluginJsonStorages[type].Save(); + } + + public void BackupJsonStorage() where T : new() + { + var type = typeof(T); + if (!PluginJsonStorages.ContainsKey(type)) + throw new InvalidOperationException("You haven't registered the JsonStorage for specific Type"); + + PluginJsonStorages[type].BackupOriginFile(); + } public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; From 1f0c0022ac45e1e9005c54d4815dbcfa0288df9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 29 Mar 2021 14:24:20 +0800 Subject: [PATCH 163/398] Increase the default width and allow user to resize setting window, and reduce the margin for PluginDirectory modified: Flow.Launcher/SettingWindow.xaml modified: Flow.Launcher/SettingWindow.xaml --- Flow.Launcher/SettingWindow.xaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index cb0ef2def..9f811e322 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -13,9 +13,9 @@ mc:Ignorable="d" Icon="Images\app.ico" Title="{DynamicResource flowlauncher_settings}" - ResizeMode="NoResize" + ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen" - Height="600" Width="800" + Height="600" Width="850" Closed="OnClosed" d:DataContext="{d:DesignInstance vm:SettingWindowViewModel}"> @@ -191,7 +191,7 @@ From 0d1bb12b6730662dcf006657c5812792b9515fa9 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 30 Mar 2021 06:51:42 +1100 Subject: [PATCH 164/398] add documentation link --- Flow.Launcher.Infrastructure/Constant.cs | 1 + Flow.Launcher/Languages/en.xaml | 1 + Flow.Launcher/SettingWindow.xaml | 10 +++++++++- Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index de6ed1b29..a5b89c300 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -21,6 +21,7 @@ namespace Flow.Launcher.Infrastructure public static readonly string PreinstalledDirectory = Path.Combine(ProgramDirectory, Plugins); public const string Issue = "https://github.com/Flow-Launcher/Flow.Launcher/issues/new"; public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location.NonNull()).ProductVersion; + public const string Documentation = "https://flow-launcher.github.io/docs/#/usage-tips"; public static readonly int ThumbnailSize = 64; private static readonly string ImagesDirectory = Path.Combine(ProgramDirectory, "Images"); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index a036949fc..7258e3a91 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -107,6 +107,7 @@ or go to https://github.com/Flow-Launcher/Flow.Launcher/releases to download updates manually. Release Notes + Usage Tips: Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index cb0ef2def..020ce7692 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -431,6 +431,7 @@ + @@ -448,7 +449,14 @@ - public class JsonRPCServerRequestModel : JsonRPCRequestModel { - public override string ToString() - { - string rpc = base.ToString(); - return rpc + "}"; - } + } /// @@ -110,12 +109,6 @@ namespace Flow.Launcher.Core.Plugin public class JsonRPCClientRequestModel : JsonRPCRequestModel { public bool DontHideAfterAction { get; set; } - - public override string ToString() - { - string rpc = base.ToString(); - return rpc + "}"; - } } /// diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 7a088bd09..90ab6f010 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -46,7 +46,6 @@ namespace Flow.Launcher.Core.Plugin } - private async Task> DeserializedResultAsync(Stream output) { if (output == Stream.Null) return null; @@ -71,7 +70,7 @@ namespace Flow.Launcher.Core.Plugin var results = new List(); if (queryResponseModel.Result == null) return null; - if(!string.IsNullOrEmpty(queryResponseModel.DebugMessage)) + if (!string.IsNullOrEmpty(queryResponseModel.DebugMessage)) { context.API.ShowMsg(queryResponseModel.DebugMessage); } @@ -177,12 +176,11 @@ namespace Flow.Launcher.Core.Plugin if (result.StartsWith("DEBUG:")) { - MessageBox.Show(new Form { TopMost = true }, result.Substring(6)); + MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); return string.Empty; } return result; - } catch (Exception e) { @@ -248,10 +246,10 @@ namespace Flow.Launcher.Core.Plugin } } - public Task InitAsync(PluginInitContext context) + public virtual Task InitAsync(PluginInitContext context) { this.context = context; return Task.CompletedTask; } } -} +} \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/PythonPlugin.cs b/Flow.Launcher.Core/Plugin/PythonPlugin.cs index 356a68a81..16d84136f 100644 --- a/Flow.Launcher.Core/Plugin/PythonPlugin.cs +++ b/Flow.Launcher.Core/Plugin/PythonPlugin.cs @@ -28,17 +28,19 @@ namespace Flow.Launcher.Core.Plugin var path = Path.Combine(Constant.ProgramDirectory, JsonRPC); _startInfo.EnvironmentVariables["PYTHONPATH"] = path; + //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"); } protected override Task ExecuteQueryAsync(Query query, CancellationToken token) { JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { - Method = "query", - Parameters = new object[] { query.Search }, + Method = "query", Parameters = new object[] {query.Search}, }; - //Add -B flag to tell python don't write .py[co] files. Because .pyc contains location infos which will prevent python portable - _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{request}\""; + + _startInfo.ArgumentList[2] = request.ToString(); + // todo happlebao why context can't be used in constructor _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; @@ -47,16 +49,17 @@ namespace Flow.Launcher.Core.Plugin protected override string ExecuteCallback(JsonRPCRequestModel rpcRequest) { - _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{rpcRequest}\""; + _startInfo.ArgumentList[2] = rpcRequest.ToString(); _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; // TODO: Async Action return Execute(_startInfo); } - protected override string ExecuteContextMenu(Result selectedResult) { - JsonRPCServerRequestModel request = new JsonRPCServerRequestModel { - Method = "context_menu", - Parameters = new object[] { selectedResult.ContextData }, + protected override string ExecuteContextMenu(Result selectedResult) + { + JsonRPCServerRequestModel request = new JsonRPCServerRequestModel + { + Method = "context_menu", Parameters = new object[] {selectedResult.ContextData}, }; _startInfo.Arguments = $"-B \"{context.CurrentPluginMetadata.ExecuteFilePath}\" \"{request}\""; _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; @@ -64,5 +67,13 @@ namespace Flow.Launcher.Core.Plugin // TODO: Async Action return Execute(_startInfo); } + + public override Task InitAsync(PluginInitContext context) + { + this.context = context; + _startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); + _startInfo.ArgumentList.Add(""); + return Task.CompletedTask; + } } } \ No newline at end of file From 18e21a8b19fe0332a15f0b745e95dee483a54053 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Mon, 7 Jun 2021 13:08:18 +0800 Subject: [PATCH 265/398] Don't thown error when JsonRPCPlugin.cs don't return a JsonFeedBack when calling callback --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 90ab6f010..11d1abb63 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -91,6 +91,8 @@ namespace Flow.Launcher.Core.Plugin else { string actionReponse = ExecuteCallback(result.JsonRPCAction); + if (string.IsNullOrEmpty(actionReponse)) + return false; JsonRPCRequestModel jsonRpcRequestModel = JsonSerializer.Deserialize(actionReponse); if (jsonRpcRequestModel != null From 456016cdd8bd6f5ebe79ce7ab255a8bd8ff1f380 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Mon, 7 Jun 2021 19:17:46 +0800 Subject: [PATCH 266/398] Use where.exe to detect pythonPath easily --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 82 +++++++++++++--------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 92aca8109..3f998fa51 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -11,6 +11,8 @@ using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; +using System.Diagnostics; +using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Core.Plugin { @@ -85,11 +87,7 @@ namespace Flow.Launcher.Core.Plugin return; } - plugins.Add(new PluginPair - { - Plugin = plugin, - Metadata = metadata - }); + plugins.Add(new PluginPair {Plugin = plugin, Metadata = metadata}); }); metadata.InitTime += milliseconds; } @@ -123,24 +121,42 @@ namespace Flow.Launcher.Core.Plugin // PATH or from the given pythonDirectory if (string.IsNullOrEmpty(settings.PythonDirectory)) { - var paths = Environment.GetEnvironmentVariable(PATH); - if (paths != null) + var whereProcess = Process.Start(new ProcessStartInfo { - var pythonInPath = paths - .Split(';') - .Where(p => p.ToLower().Contains(Python)) - .Any(); + FileName = "where.exe", + Arguments = "pythonw", + RedirectStandardOutput = true, + CreateNoWindow = true, + UseShellExecute = false + }); - if (pythonInPath) - { - Constant.PythonPath = - Path.Combine(paths.Split(';').Where(p => p.ToLower().Contains(Python)).FirstOrDefault(), PythonExecutable); - settings.PythonDirectory = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, Constant.PythonPath); - } - else - { - Log.Error("PluginsLoader", "Failed to set Python path despite the environment variable PATH is found", "PythonPlugins"); - } + var pythonPath = whereProcess?.StandardOutput.ReadToEnd().Trim(); + + if (!string.IsNullOrEmpty(pythonPath)) + { + pythonPath = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, pythonPath); + } + + if (string.IsNullOrEmpty(pythonPath)) + { + var paths = Environment.GetEnvironmentVariable(PATH); + + pythonPath = paths? + .Split(';') + .FirstOrDefault(p => p.ToLower().Contains(Python)); + } + + if (!string.IsNullOrEmpty(pythonPath)) + { + Constant.PythonPath = Path.Combine(pythonPath, PythonExecutable); + settings.PythonDirectory = + FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, Constant.PythonPath); + } + else + { + Log.Error("PluginsLoader", + "Failed to set Python path despite the environment variable PATH is found", + "PythonPlugins"); } } else @@ -153,17 +169,17 @@ namespace Flow.Launcher.Core.Plugin else { Log.Error("PluginsLoader", $"Tried to automatically set from Settings.PythonDirectory " + - $"but can't find python executable in {path}", "PythonPlugins"); + $"but can't find python executable in {path}", "PythonPlugins"); } } if (string.IsNullOrEmpty(settings.PythonDirectory)) { if (MessageBox.Show("Flow detected you have installed Python plugins, " + - "would you like to install Python to run them? " + - Environment.NewLine + Environment.NewLine + - "Click no if it's already installed, " + - "and you will be prompted to select the folder that contains the Python executable", + "would you like to install Python to run them? " + + Environment.NewLine + Environment.NewLine + + "Click no if it's already installed, " + + "and you will be prompted to select the folder that contains the Python executable", string.Empty, MessageBoxButtons.YesNo) == DialogResult.No && string.IsNullOrEmpty(settings.PythonDirectory)) { @@ -196,7 +212,8 @@ namespace Flow.Launcher.Core.Plugin DroplexPackage.Drop(App.python3_9_1).Wait(); var installedPythonDirectory = - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39"); + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + @"Programs\Python\Python39"); var pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable); if (FilesFolders.FileExists(pythonPath)) { @@ -214,7 +231,8 @@ namespace Flow.Launcher.Core.Plugin if (string.IsNullOrEmpty(settings.PythonDirectory)) { - MessageBox.Show("Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom)."); + MessageBox.Show( + "Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom)."); Log.Error("PluginsLoader", $"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.", "PythonPlugins"); @@ -226,8 +244,7 @@ namespace Flow.Launcher.Core.Plugin .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) .Select(metadata => new PluginPair { - Plugin = new PythonPlugin(Constant.PythonPath), - Metadata = metadata + Plugin = new PythonPlugin(Constant.PythonPath), Metadata = metadata }) .ToList(); } @@ -238,9 +255,8 @@ namespace Flow.Launcher.Core.Plugin .Where(o => o.Language.ToUpper() == AllowedLanguage.Executable) .Select(metadata => new PluginPair { - Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), - Metadata = metadata + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata }); } } -} +} \ No newline at end of file From c399a872798f9521106ff5d353d1b5bc2323afdc Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Tue, 8 Jun 2021 08:02:25 +0800 Subject: [PATCH 267/398] Add Customized Object Converter, fix Length issue --- Flow.Launcher.Core/Plugin/JsonPRCModel.cs | 2 + Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 15 ++++--- .../Resource/JsonObjectConverter.cs | 42 +++++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 Flow.Launcher.Core/Resource/JsonObjectConverter.cs diff --git a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs index 27734c7f7..1dac4e25d 100644 --- a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs +++ b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs @@ -13,6 +13,7 @@ * */ +using Flow.Launcher.Core.Resource; using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; @@ -108,6 +109,7 @@ namespace Flow.Launcher.Core.Plugin /// public class JsonRPCClientRequestModel : JsonRPCRequestModel { + [JsonPropertyName("dontHideAfterAction")] public bool DontHideAfterAction { get; set; } } diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 11d1abb63..875f4fb3e 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -1,4 +1,5 @@ -using System; +using Flow.Launcher.Core.Resource; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -45,13 +46,14 @@ namespace Flow.Launcher.Core.Plugin } } + private static readonly JsonSerializerOptions _options = new() {Converters = {new JsonObjectConverter()}}; private async Task> DeserializedResultAsync(Stream output) { if (output == Stream.Null) return null; - JsonRPCQueryResponseModel queryResponseModel = await - JsonSerializer.DeserializeAsync(output); + var queryResponseModel = await + JsonSerializer.DeserializeAsync(output, _options); return ParseResults(queryResponseModel); } @@ -60,11 +62,12 @@ namespace Flow.Launcher.Core.Plugin { if (string.IsNullOrEmpty(output)) return null; - JsonRPCQueryResponseModel queryResponseModel = - JsonSerializer.Deserialize(output); + var queryResponseModel = + JsonSerializer.Deserialize(output, _options); return ParseResults(queryResponseModel); } + private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) { var results = new List(); @@ -85,7 +88,7 @@ namespace Flow.Launcher.Core.Plugin { if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) { - ExecuteFlowLauncherAPI(result.JsonRPCAction.Method.Substring(4), + ExecuteFlowLauncherAPI(result.JsonRPCAction.Method[14..], result.JsonRPCAction.Parameters); } else diff --git a/Flow.Launcher.Core/Resource/JsonObjectConverter.cs b/Flow.Launcher.Core/Resource/JsonObjectConverter.cs new file mode 100644 index 000000000..5a891ae30 --- /dev/null +++ b/Flow.Launcher.Core/Resource/JsonObjectConverter.cs @@ -0,0 +1,42 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Flow.Launcher.Core.Resource +{ + public class JsonObjectConverter : JsonConverter + { + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.True: + return true; + case JsonTokenType.False: + return false; + case JsonTokenType.Number when reader.TryGetInt32(out var i): + return i; + case JsonTokenType.Number when reader.TryGetInt64(out var l): + return l; + case JsonTokenType.Number: + return reader.GetDouble(); + case JsonTokenType.String when reader.TryGetDateTime(out DateTime datetime): + return datetime; + case JsonTokenType.String: + return reader.GetString(); + default: + // Use JsonElement as fallback. + // Newtonsoft uses JArray or JObject. + using (var document = JsonDocument.ParseValue(ref reader)) + { + return document.RootElement.Clone(); + } + } + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + throw new InvalidOperationException("Should not get here."); + } + } +} \ No newline at end of file From aa3e7decd80e12c5c6e1add5e311237b4a4aa0f2 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 10 Jun 2021 11:21:58 +0800 Subject: [PATCH 268/398] Fix Don'tHideAfterAction logic for JsonRPCPlugin.cs --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 41 +++++++++++++--------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 875f4fb3e..f684bc6aa 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -84,27 +84,34 @@ namespace Flow.Launcher.Core.Plugin { if (result.JsonRPCAction == null) return false; - if (!string.IsNullOrEmpty(result.JsonRPCAction.Method)) + if (string.IsNullOrEmpty(result.JsonRPCAction.Method)) { - if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) + return !result.JsonRPCAction.DontHideAfterAction; + } + + if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) + { + ExecuteFlowLauncherAPI(result.JsonRPCAction.Method[14..], + result.JsonRPCAction.Parameters); + } + else + { + var actionResponse = ExecuteCallback(result.JsonRPCAction); + + if (string.IsNullOrEmpty(actionResponse)) { - ExecuteFlowLauncherAPI(result.JsonRPCAction.Method[14..], - result.JsonRPCAction.Parameters); + return !result.JsonRPCAction.DontHideAfterAction; } - else + + JsonRPCRequestModel jsonRpcRequestModel = + JsonSerializer.Deserialize(actionResponse); + + if (jsonRpcRequestModel != null + && !string.IsNullOrEmpty(jsonRpcRequestModel.Method) + && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) { - string actionReponse = ExecuteCallback(result.JsonRPCAction); - if (string.IsNullOrEmpty(actionReponse)) - return false; - JsonRPCRequestModel jsonRpcRequestModel = - JsonSerializer.Deserialize(actionReponse); - if (jsonRpcRequestModel != null - && !string.IsNullOrEmpty(jsonRpcRequestModel.Method) - && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) - { - ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), - jsonRpcRequestModel.Parameters); - } + ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), + jsonRpcRequestModel.Parameters); } } From be8abafddaf6d5d0f1494dfafa246ecb9848d1bb Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 10 Jun 2021 13:06:35 +0800 Subject: [PATCH 269/398] fix another hardcode issue, and the JsonSerialization problem --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 30 +++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index f684bc6aa..ff9c11a94 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -46,7 +46,13 @@ namespace Flow.Launcher.Core.Plugin } } - private static readonly JsonSerializerOptions _options = new() {Converters = {new JsonObjectConverter()}}; + private static readonly JsonSerializerOptions _options = new() + { + Converters = + { + new JsonObjectConverter() + } + }; private async Task> DeserializedResultAsync(Stream output) { @@ -88,10 +94,10 @@ namespace Flow.Launcher.Core.Plugin { return !result.JsonRPCAction.DontHideAfterAction; } - + if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher.")) { - ExecuteFlowLauncherAPI(result.JsonRPCAction.Method[14..], + ExecuteFlowLauncherAPI(result.JsonRPCAction.Method["Flow.Launcher.".Length..], result.JsonRPCAction.Parameters); } else @@ -102,15 +108,12 @@ namespace Flow.Launcher.Core.Plugin { return !result.JsonRPCAction.DontHideAfterAction; } - - JsonRPCRequestModel jsonRpcRequestModel = - JsonSerializer.Deserialize(actionResponse); - - if (jsonRpcRequestModel != null - && !string.IsNullOrEmpty(jsonRpcRequestModel.Method) - && jsonRpcRequestModel.Method.StartsWith("Flow.Launcher.")) + + var jsonRpcRequestModel = JsonSerializer.Deserialize(actionResponse, _options); + + if (jsonRpcRequestModel?.Method?.StartsWith("Flow.Launcher.") ?? false) { - ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method.Substring(4), + ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method["Flow.Launcher.".Length..], jsonRpcRequestModel.Parameters); } } @@ -188,7 +191,10 @@ namespace Flow.Launcher.Core.Plugin if (result.StartsWith("DEBUG:")) { - MessageBox.Show(new Form {TopMost = true}, result.Substring(6)); + MessageBox.Show(new Form + { + TopMost = true + }, result.Substring(6)); return string.Empty; } From 5cedf7084ce5cd2c3cbe975155579d8d562656e6 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 10 Jun 2021 15:08:10 +0800 Subject: [PATCH 270/398] Remove Unused Code --- Flow.Launcher.Core/Plugin/JsonPRCModel.cs | 38 ++--------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs index 1dac4e25d..2b3bd61b0 100644 --- a/Flow.Launcher.Core/Plugin/JsonPRCModel.cs +++ b/Flow.Launcher.Core/Plugin/JsonPRCModel.cs @@ -1,5 +1,4 @@ - -/* We basically follow the Json-RPC 2.0 spec (http://www.jsonrpc.org/specification) to invoke methods between Flow Launcher and other plugins, +/* We basically follow the Json-RPC 2.0 spec (http://www.jsonrpc.org/specification) to invoke methods between Flow Launcher and other plugins, * like python or other self-execute program. But, we added addtional infos (proxy and so on) into rpc request. Also, we didn't use the * "id" and "jsonrpc" in the request, since it's not so useful in our request model. * @@ -62,37 +61,6 @@ namespace Flow.Launcher.Core.Plugin public override string ToString() { return JsonSerializer.Serialize(this); - - string rpc = string.Empty; - if (Parameters != null && Parameters.Length > 0) - { - string parameters = $"[{string.Join(',', Parameters.Select(GetParameterByType))}]"; - rpc = $@"{{\""method\"":\""{Method}\"",\""parameters\"":{parameters}"; - } - else - { - rpc = $@"{{\""method\"":\""{Method}\"",\""parameters\"":[]"; - } - - return rpc; - - } - - private string GetParameterByType(object parameter) - => parameter switch - { - null => "null", - string p => $@"\""{ReplaceEscapes(p)}\""", - bool p => $@"{p.ToString().ToLower()}", - _ => $@"\""{ReplaceEscapes(parameter.ToString())}\""" - }; - - - private string ReplaceEscapes(string str) - { - return str.Replace(@"\", @"\\") //Escapes in ProcessStartInfo - .Replace(@"\", @"\\") //Escapes itself when passed to client - .Replace(@"""", @"\\"""""); } } @@ -101,7 +69,7 @@ namespace Flow.Launcher.Core.Plugin /// public class JsonRPCServerRequestModel : JsonRPCRequestModel { - + } /// @@ -122,4 +90,4 @@ namespace Flow.Launcher.Core.Plugin { public JsonRPCClientRequestModel JsonRPCAction { get; set; } } -} +} \ No newline at end of file From ef73c926d02cd674277bfa8c04562dd4d30d2897 Mon Sep 17 00:00:00 2001 From: Zelin Liao Date: Sat, 12 Jun 2021 22:38:40 +0800 Subject: [PATCH 271/398] Add Margin When Enabling Box Shadows --- Flow.Launcher.Core/Resource/Theme.cs | 41 +++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index a5f12bbb9..fe64e0c3e 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -17,6 +17,8 @@ namespace Flow.Launcher.Core.Resource { public class Theme { + private const int ShadowExtraMargin = 12; + private readonly List _themeDirectories = new List(); private ResourceDictionary _oldResource; private string _oldTheme; @@ -224,6 +226,27 @@ namespace Flow.Launcher.Core.Resource BlurRadius = 15 }; + var marginSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) as Setter; + if (marginSetter == null) + { + marginSetter = new Setter() + { + Property = Border.MarginProperty, + Value = new Thickness(ShadowExtraMargin), + }; + windowBorderStyle.Setters.Add(marginSetter); + } + else + { + var baseMargin = (Thickness) marginSetter.Value; + var newMargin = new Thickness( + baseMargin.Left + ShadowExtraMargin, + baseMargin.Top + ShadowExtraMargin, + baseMargin.Right + ShadowExtraMargin, + baseMargin.Bottom + ShadowExtraMargin); + marginSetter.Value = newMargin; + } + windowBorderStyle.Setters.Add(effectSetter); UpdateResourceDictionary(dict); @@ -234,7 +257,23 @@ namespace Flow.Launcher.Core.Resource var dict = CurrentThemeResourceDictionary(); var windowBorderStyle = dict["WindowBorderStyle"] as Style; - dict.Remove(Border.EffectProperty); + var effectSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) as Setter; + var marginSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) as Setter; + + if(effectSetter != null) + { + windowBorderStyle.Setters.Remove(effectSetter); + } + if (marginSetter != null) + { + var currentMargin = (Thickness)marginSetter.Value; + var newMargin = new Thickness( + currentMargin.Left - ShadowExtraMargin, + currentMargin.Top - ShadowExtraMargin, + currentMargin.Right - ShadowExtraMargin, + currentMargin.Bottom - ShadowExtraMargin); + marginSetter.Value = newMargin; + } UpdateResourceDictionary(dict); } From 596b5b803ecb75864c2221f85b9b8ca0c2399b03 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 13 Jun 2021 17:27:44 +1000 Subject: [PATCH 272/398] update logic when content search is global --- .../Views/ActionKeywordSetting.xaml.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index 5b733fe5c..fe73587c7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -67,11 +67,8 @@ namespace Flow.Launcher.Plugin.Explorer.Views } - if (CurrentActionKeyword is - { - Keyword: Query.GlobalPluginWildcardSign, - KeywordProperty: Settings.ActionKeyword.FileContentSearchActionKeyword - }) + if (CurrentActionKeyword.KeywordProperty == Settings.ActionKeyword.FileContentSearchActionKeyword + && ActionKeyword == Query.GlobalPluginWildcardSign) { MessageBox.Show( settingsViewModel.Context.API.GetTranslation("plugin_explorer_globalActionKeywordInvalid")); From 69f58518b2685a3d96b347015c6919bb1c838d24 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 13 Jun 2021 17:41:44 +1000 Subject: [PATCH 273/398] update revert to global wildcard logic --- .../Views/ActionKeywordSetting.xaml.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index fe73587c7..9d7f08e69 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -78,12 +78,11 @@ namespace Flow.Launcher.Plugin.Explorer.Views var oldActionKeyword = CurrentActionKeyword.Keyword; - // == because of nullable if (Enabled == false || !settingsViewModel.IsActionKeywordAlreadyAssigned(ActionKeyword)) { // Update View Data - CurrentActionKeyword.Keyword = ActionKeyword; + CurrentActionKeyword.Keyword = Enabled == true ? ActionKeyword : Query.GlobalPluginWildcardSign; CurrentActionKeyword.Enabled = Enabled; switch (Enabled) From d807b21eaca54d394f62186c28fcf059c2c7bb83 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 13 Jun 2021 17:53:55 +1000 Subject: [PATCH 274/398] remove obsolete settings class --- .../Views/ActionKeywordSetting.xaml.cs | 6 +----- .../Views/ExplorerSettings.xaml.cs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs index 9d7f08e69..18703e5ea 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs @@ -31,20 +31,16 @@ namespace Flow.Launcher.Plugin.Explorer.Views public bool? Enabled { get; set; } - - private Settings settings; private string _actionKeyword; public Visibility Visible => CurrentActionKeyword.Enabled is not null ? Visibility.Visible : Visibility.Collapsed; public ActionKeywordSetting(SettingsViewModel settingsViewModel, - ActionKeywordView selectedActionKeyword, Settings settings) + ActionKeywordView selectedActionKeyword) { this.settingsViewModel = settingsViewModel; - this.settings = settings; - CurrentActionKeyword = selectedActionKeyword; ActionKeyword = selectedActionKeyword.Keyword; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs index aa36f4b54..26a53f7fb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs @@ -193,7 +193,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views var selectedActionKeyword = lbxActionKeywords.SelectedItem as ActionKeywordView; var actionKeywordWindow = new ActionKeywordSetting(viewModel, - selectedActionKeyword, viewModel.Settings); + selectedActionKeyword); actionKeywordWindow.ShowDialog(); From c842a4c7bf785616947fbbd6decee097068b9410 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 13 Jun 2021 18:07:30 +1000 Subject: [PATCH 275/398] clean up --- Flow.Launcher.Test/Flow.Launcher.Test.csproj | 4 ---- Flow.Launcher.sln | 1 - Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml | 4 ++-- Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml | 2 +- .../Views/ExplorerSettings.xaml.cs | 4 ++-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 5906e6093..1d0dce5e7 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -38,10 +38,6 @@ - - - - diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 46c4488a1..21c3b47dc 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -45,7 +45,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Url", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FFD651C7-0546-441F-BC8C-D4EE8FD01EA7}" ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore appveyor.yml = appveyor.yml diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index 24a591c4f..a989327be 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -20,9 +20,9 @@ Index Search Excluded Paths Indexing Options Search: - Path Search: + Path Search: File Content Search: - Index Search: + Index Search: Current Action Keyword: Done Enabled diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml index 1aed9bf29..dac6e908b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml @@ -20,7 +20,7 @@ 索引搜索排除的路径 索引选项 搜索激活: - 路径搜索激活: + 路径搜索激活: 文件内容搜索: diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs index 26a53f7fb..ffd5b618a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs @@ -40,9 +40,9 @@ namespace Flow.Launcher.Plugin.Explorer.Views new(Settings.ActionKeyword.FileContentSearchActionKeyword, viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_filecontentsearch")), new(Settings.ActionKeyword.PathSearchActionKeyword, - viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_path")), + viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_pathsearch")), new(Settings.ActionKeyword.IndexSearchActionKeyword, - viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_indexonlysearch")) + viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_indexsearch")) }; lbxActionKeywords.ItemsSource = actionKeywordsListView; From 9d2199319fcd2fa3bf92478e11401e405ba52c17 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sun, 13 Jun 2021 18:25:02 +1000 Subject: [PATCH 276/398] Explorer plugin version bump --- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 0b46f9fd7..5bd49949f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -9,7 +9,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.7.7", + "Version": "1.8.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", From 797ff90195b9422d3eca9d3195384a017c26d504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 15 Jun 2021 16:41:27 +0800 Subject: [PATCH 277/398] fix storyboard animation control --- Flow.Launcher/MainWindow.xaml.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 04a1063f8..49e14fa46 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -88,13 +88,13 @@ namespace Flow.Launcher if (_viewModel.ProgressBarVisibility == Visibility.Visible && isProgressBarStoryboardPaused) { - _progressBarStoryboard.Resume(); + _progressBarStoryboard.Resume(ProgressBar); isProgressBarStoryboardPaused = false; } } else if (!isProgressBarStoryboardPaused) { - _progressBarStoryboard.Pause(); + _progressBarStoryboard.Pause(ProgressBar); isProgressBarStoryboardPaused = true; } } @@ -104,12 +104,13 @@ namespace Flow.Launcher { if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) { - _progressBarStoryboard.Pause(); + _progressBarStoryboard.Pause(ProgressBar); isProgressBarStoryboardPaused = true; } else if (_viewModel.MainWindowVisibility == Visibility.Visible && isProgressBarStoryboardPaused) { - _progressBarStoryboard.Resume(); + + _progressBarStoryboard.Resume(ProgressBar); isProgressBarStoryboardPaused = false; } }, System.Windows.Threading.DispatcherPriority.Render); @@ -196,7 +197,10 @@ namespace Flow.Launcher _progressBarStoryboard.Children.Add(da); _progressBarStoryboard.Children.Add(da1); _progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever; - ProgressBar.BeginStoryboard(_progressBarStoryboard); + + _progressBarStoryboard.Begin(ProgressBar, true); + _progressBarStoryboard.Pause(); + _viewModel.ProgressBarVisibility = Visibility.Hidden; isProgressBarStoryboardPaused = true; } From 4684a38883b36df2509ab75f56ec2bc78e02e617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 15 Jun 2021 17:27:21 +0800 Subject: [PATCH 278/398] 1. Change propertyname match to switch, 2. Use begin/stop instead of resume/pause to reset position 3. Add a small delay when stopping the animation after hiding to make the animation more fluently --- Flow.Launcher/MainWindow.xaml.cs | 93 +++++++++++++++++--------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 49e14fa46..51e1574f9 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media.Animation; @@ -22,7 +23,6 @@ namespace Flow.Launcher { public partial class MainWindow { - #region Private Fields private readonly Storyboard _progressBarStoryboard = new Storyboard(); @@ -40,6 +40,7 @@ namespace Flow.Launcher _settings = settings; InitializeComponent(); } + public MainWindow() { InitializeComponent(); @@ -53,7 +54,6 @@ namespace Flow.Launcher private void OnInitialized(object sender, EventArgs e) { - } private void OnLoaded(object sender, RoutedEventArgs _) @@ -72,48 +72,57 @@ namespace Flow.Launcher _viewModel.PropertyChanged += (o, e) => { - if (e.PropertyName == nameof(MainViewModel.MainWindowVisibility)) + switch (e.PropertyName) { - if (_viewModel.MainWindowVisibility == Visibility.Visible) + case nameof(MainViewModel.MainWindowVisibility): { - Activate(); - QueryTextBox.Focus(); - UpdatePosition(); - _settings.ActivateTimes++; - if (!_viewModel.LastQuerySelected) + if (_viewModel.MainWindowVisibility == Visibility.Visible) { - QueryTextBox.SelectAll(); - _viewModel.LastQuerySelected = true; + Activate(); + QueryTextBox.Focus(); + UpdatePosition(); + _settings.ActivateTimes++; + if (!_viewModel.LastQuerySelected) + { + QueryTextBox.SelectAll(); + _viewModel.LastQuerySelected = true; + } + + if (_viewModel.ProgressBarVisibility == Visibility.Visible && isProgressBarStoryboardPaused) + { + _progressBarStoryboard.Begin(ProgressBar); + isProgressBarStoryboardPaused = false; + } } - if (_viewModel.ProgressBarVisibility == Visibility.Visible && isProgressBarStoryboardPaused) + if (!isProgressBarStoryboardPaused) { - _progressBarStoryboard.Resume(ProgressBar); - isProgressBarStoryboardPaused = false; - } - } - else if (!isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Pause(ProgressBar); - isProgressBarStoryboardPaused = true; - } - } - else if (e.PropertyName == nameof(MainViewModel.ProgressBarVisibility)) - { - Dispatcher.Invoke(() => - { - if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Pause(ProgressBar); + _progressBarStoryboard.Stop(ProgressBar); isProgressBarStoryboardPaused = true; } - else if (_viewModel.MainWindowVisibility == Visibility.Visible && isProgressBarStoryboardPaused) + + break; + } + case nameof(MainViewModel.ProgressBarVisibility): + { + Dispatcher.Invoke(async () => { - - _progressBarStoryboard.Resume(ProgressBar); - isProgressBarStoryboardPaused = false; - } - }, System.Windows.Threading.DispatcherPriority.Render); + if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) + { + await Task.Delay(50); + _progressBarStoryboard.Stop(ProgressBar); + isProgressBarStoryboardPaused = true; + } + else if (_viewModel.MainWindowVisibility == Visibility.Visible && + isProgressBarStoryboardPaused) + { + _progressBarStoryboard.Begin(ProgressBar); + isProgressBarStoryboardPaused = false; + } + }, System.Windows.Threading.DispatcherPriority.Render); + + break; + } } }; _settings.PropertyChanged += (o, e) => @@ -190,17 +199,15 @@ namespace Flow.Launcher private void InitProgressbarAnimation() { - var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100, + new Duration(new TimeSpan(0, 0, 0, 0, 1600))); var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); _progressBarStoryboard.Children.Add(da); _progressBarStoryboard.Children.Add(da1); _progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever; - - _progressBarStoryboard.Begin(ProgressBar, true); - _progressBarStoryboard.Pause(); - + _viewModel.ProgressBarVisibility = Visibility.Hidden; isProgressBarStoryboardPaused = true; } @@ -214,10 +221,10 @@ namespace Flow.Launcher { if (sender != null && e.OriginalSource != null) { - var r = (ResultListBox)sender; - var d = (DependencyObject)e.OriginalSource; + var r = (ResultListBox) sender; + var d = (DependencyObject) e.OriginalSource; var item = ItemsControl.ContainerFromElement(r, d) as ListBoxItem; - var result = (ResultViewModel)item?.DataContext; + var result = (ResultViewModel) item?.DataContext; if (result != null) { if (e.ChangedButton == MouseButton.Left) From c8e4d488a4b00c0458de953f2254ebe2aeabe189 Mon Sep 17 00:00:00 2001 From: Roey Darwish Dror Date: Wed, 16 Jun 2021 10:36:19 +0300 Subject: [PATCH 279/398] Fix plugin documentation link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b985e9b1f..ed85d7616 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Windows may complain about security due to code not being signed, this will be c ## Plugins -There is a [list of actively maintained plugins](https://github.com/Flow-Launcher/docs/blob/main/Plugins.md) in the documentation section, some of which are integrated from other launchers. +There is a [list of actively maintained plugins](https://github.com/Flow-Launcher/docs/blob/main/plugins.md) in the documentation section, some of which are integrated from other launchers. ## Status From bd66e56e7cb514dad646069420123d4e65a2bc99 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Wed, 16 Jun 2021 23:27:34 +0800 Subject: [PATCH 280/398] add controllable argument to allow stop --- Flow.Launcher/MainWindow.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 51e1574f9..4355cda15 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -90,7 +90,7 @@ namespace Flow.Launcher if (_viewModel.ProgressBarVisibility == Visibility.Visible && isProgressBarStoryboardPaused) { - _progressBarStoryboard.Begin(ProgressBar); + _progressBarStoryboard.Begin(ProgressBar, true); isProgressBarStoryboardPaused = false; } } @@ -116,7 +116,7 @@ namespace Flow.Launcher else if (_viewModel.MainWindowVisibility == Visibility.Visible && isProgressBarStoryboardPaused) { - _progressBarStoryboard.Begin(ProgressBar); + _progressBarStoryboard.Begin(ProgressBar, true); isProgressBarStoryboardPaused = false; } }, System.Windows.Threading.DispatcherPriority.Render); From cc2b9dc81d22226e2479084ae2cc238551802868 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Thu, 17 Jun 2021 22:20:32 +1000 Subject: [PATCH 281/398] use python 3.8.9 embeddable --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 2 +- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 2b2bef4cf..ac49b193c 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -53,7 +53,7 @@ - + diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 3f998fa51..fa34db7a4 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -209,7 +209,8 @@ namespace Flow.Launcher.Core.Plugin } else { - DroplexPackage.Drop(App.python3_9_1).Wait(); + // Python 3.8.9 is used for Windows 7 compatibility + DroplexPackage.Drop(App.python_3_8_9_embeddable, Path.Combine(DataLocation.DataDirectory(), "Python Embeddable")).Wait(); var installedPythonDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), From 4909f582254c53a6901dc4bf533c4e66abcf07af Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 18 Jun 2021 08:27:23 +1000 Subject: [PATCH 282/398] switch to embedded path --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index fa34db7a4..87abc9e36 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -209,12 +209,11 @@ namespace Flow.Launcher.Core.Plugin } else { - // Python 3.8.9 is used for Windows 7 compatibility - DroplexPackage.Drop(App.python_3_8_9_embeddable, Path.Combine(DataLocation.DataDirectory(), "Python Embeddable")).Wait(); + var installedPythonDirectory = Path.Combine(DataLocation.DataDirectory(), "Python Embeddable"); + + // Python 3.8.9 is used for Windows 7 compatibility + DroplexPackage.Drop(App.python_3_8_9_embeddable, installedPythonDirectory).Wait(); - var installedPythonDirectory = - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - @"Programs\Python\Python39"); var pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable); if (FilesFolders.FileExists(pythonPath)) { From 64f6c0f60e73c11d9c2af0ee20ae1f94278e7bff Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 18 Jun 2021 22:48:30 +1000 Subject: [PATCH 283/398] update Droplex --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index ac49b193c..8d1408449 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -53,7 +53,7 @@ - + From c9b232aa265a6854a2d9b16fc064eb336f450234 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 18 Jun 2021 22:58:14 +1000 Subject: [PATCH 284/398] remove automatic retrieval py path from PATH or via where.exe --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 58 ---------------------- 1 file changed, 58 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 87abc9e36..5d9b987f1 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -18,8 +18,6 @@ namespace Flow.Launcher.Core.Plugin { public static class PluginsLoader { - public const string PATH = "PATH"; - public const string Python = "python"; public const string PythonExecutable = "pythonw.exe"; public static List Plugins(List metadatas, PluginsSettings settings) @@ -117,62 +115,6 @@ namespace Flow.Launcher.Core.Plugin if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python)) return new List(); - // Try setting Constant.PythonPath first, either from - // PATH or from the given pythonDirectory - if (string.IsNullOrEmpty(settings.PythonDirectory)) - { - var whereProcess = Process.Start(new ProcessStartInfo - { - FileName = "where.exe", - Arguments = "pythonw", - RedirectStandardOutput = true, - CreateNoWindow = true, - UseShellExecute = false - }); - - var pythonPath = whereProcess?.StandardOutput.ReadToEnd().Trim(); - - if (!string.IsNullOrEmpty(pythonPath)) - { - pythonPath = FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, pythonPath); - } - - if (string.IsNullOrEmpty(pythonPath)) - { - var paths = Environment.GetEnvironmentVariable(PATH); - - pythonPath = paths? - .Split(';') - .FirstOrDefault(p => p.ToLower().Contains(Python)); - } - - if (!string.IsNullOrEmpty(pythonPath)) - { - Constant.PythonPath = Path.Combine(pythonPath, PythonExecutable); - settings.PythonDirectory = - FilesFolders.GetPreviousExistingDirectory(FilesFolders.LocationExists, Constant.PythonPath); - } - else - { - Log.Error("PluginsLoader", - "Failed to set Python path despite the environment variable PATH is found", - "PythonPlugins"); - } - } - else - { - var path = Path.Combine(settings.PythonDirectory, PythonExecutable); - if (File.Exists(path)) - { - Constant.PythonPath = path; - } - else - { - Log.Error("PluginsLoader", $"Tried to automatically set from Settings.PythonDirectory " + - $"but can't find python executable in {path}", "PythonPlugins"); - } - } - if (string.IsNullOrEmpty(settings.PythonDirectory)) { if (MessageBox.Show("Flow detected you have installed Python plugins, " + From e125d59c6e309b3c099441f764347eb35b6ced1e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 19 Jun 2021 00:54:04 +1000 Subject: [PATCH 285/398] reorder logic for setting python path --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 106 +++++++++++---------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 5d9b987f1..7c3e50fd4 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -115,63 +115,65 @@ namespace Flow.Launcher.Core.Plugin if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python)) return new List(); - if (string.IsNullOrEmpty(settings.PythonDirectory)) - { - if (MessageBox.Show("Flow detected you have installed Python plugins, " + - "would you like to install Python to run them? " + - Environment.NewLine + Environment.NewLine + - "Click no if it's already installed, " + - "and you will be prompted to select the folder that contains the Python executable", - string.Empty, MessageBoxButtons.YesNo) == DialogResult.No - && string.IsNullOrEmpty(settings.PythonDirectory)) - { - var dlg = new FolderBrowserDialog - { - SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) - }; + if (!string.IsNullOrEmpty(settings.PythonDirectory)) + return SetPythonPathForPluginPairs(source, Path.Combine(settings.PythonDirectory, PythonExecutable)); - var result = dlg.ShowDialog(); - if (result == DialogResult.OK) + var pythonPath = string.Empty; + + if (MessageBox.Show("Flow detected you have installed Python plugins, " + + "would you like to install Python to run them? " + + Environment.NewLine + Environment.NewLine + + "Click no if it's already installed, " + + "and you will be prompted to select the folder that contains the Python executable", + string.Empty, MessageBoxButtons.YesNo) == DialogResult.No + && string.IsNullOrEmpty(settings.PythonDirectory)) + { + var dlg = new FolderBrowserDialog + { + SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + }; + + var result = dlg.ShowDialog(); + if (result == DialogResult.OK) + { + string pythonDirectory = dlg.SelectedPath; + if (!string.IsNullOrEmpty(pythonDirectory)) { - string pythonDirectory = dlg.SelectedPath; - if (!string.IsNullOrEmpty(pythonDirectory)) + pythonPath = Path.Combine(pythonDirectory, PythonExecutable); + if (File.Exists(pythonPath)) { - var pythonPath = Path.Combine(pythonDirectory, PythonExecutable); - if (File.Exists(pythonPath)) - { - settings.PythonDirectory = pythonDirectory; - Constant.PythonPath = pythonPath; - } - else - { - MessageBox.Show("Can't find python in given directory"); - } + settings.PythonDirectory = pythonDirectory; + Constant.PythonPath = pythonPath; + } + else + { + MessageBox.Show("Can't find python in given directory"); } } } + } + else + { + var installedPythonDirectory = Path.Combine(DataLocation.DataDirectory(), "Python Embeddable"); + + // Python 3.8.9 is used for Windows 7 compatibility + DroplexPackage.Drop(App.python_3_8_9_embeddable, installedPythonDirectory).Wait(); + + pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable); + if (FilesFolders.FileExists(pythonPath)) + { + settings.PythonDirectory = installedPythonDirectory; + Constant.PythonPath = pythonPath; + } else { - var installedPythonDirectory = Path.Combine(DataLocation.DataDirectory(), "Python Embeddable"); - - // Python 3.8.9 is used for Windows 7 compatibility - DroplexPackage.Drop(App.python_3_8_9_embeddable, installedPythonDirectory).Wait(); - - var pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable); - if (FilesFolders.FileExists(pythonPath)) - { - settings.PythonDirectory = installedPythonDirectory; - Constant.PythonPath = pythonPath; - } - else - { - Log.Error("PluginsLoader", - $"Failed to set Python path after Droplex install, {pythonPath} does not exist", - "PythonPlugins"); - } + Log.Error("PluginsLoader", + $"Failed to set Python path after Droplex install, {pythonPath} does not exist", + "PythonPlugins"); } } - if (string.IsNullOrEmpty(settings.PythonDirectory)) + if (string.IsNullOrEmpty(settings.PythonDirectory) || string.IsNullOrEmpty(pythonPath)) { MessageBox.Show( "Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom)."); @@ -182,16 +184,20 @@ namespace Flow.Launcher.Core.Plugin return new List(); } - return source + return SetPythonPathForPluginPairs(source, pythonPath); + } + + private static IEnumerable SetPythonPathForPluginPairs(List source, string pythonPath) + => source .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) .Select(metadata => new PluginPair { - Plugin = new PythonPlugin(Constant.PythonPath), Metadata = metadata + Plugin = new PythonPlugin(pythonPath), + Metadata = metadata }) .ToList(); - } - public static IEnumerable ExecutablePlugins(IEnumerable source) + public static IEnumerable ExecutablePlugins(IEnumerable source) { return source .Where(o => o.Language.ToUpper() == AllowedLanguage.Executable) From 5fbde3779d9d1c235a5c56fee55c00f2eead0e33 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 19 Jun 2021 11:17:00 +1000 Subject: [PATCH 286/398] check location exists if python path is already set --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 7c3e50fd4..e5f44e4c4 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -115,7 +115,7 @@ namespace Flow.Launcher.Core.Plugin if (!source.Any(o => o.Language.ToUpper() == AllowedLanguage.Python)) return new List(); - if (!string.IsNullOrEmpty(settings.PythonDirectory)) + if (!string.IsNullOrEmpty(settings.PythonDirectory) && FilesFolders.LocationExists(settings.PythonDirectory)) return SetPythonPathForPluginPairs(source, Path.Combine(settings.PythonDirectory, PythonExecutable)); var pythonPath = string.Empty; From e6db8b85e8fcf7e727f8f8ea254561af7a95c1a6 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Sat, 19 Jun 2021 19:09:09 +1000 Subject: [PATCH 287/398] remove spaces in Python Embeddable folder name --- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index e5f44e4c4..1b78c68ae 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -154,7 +154,7 @@ namespace Flow.Launcher.Core.Plugin } else { - var installedPythonDirectory = Path.Combine(DataLocation.DataDirectory(), "Python Embeddable"); + var installedPythonDirectory = Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable"); // Python 3.8.9 is used for Windows 7 compatibility DroplexPackage.Drop(App.python_3_8_9_embeddable, installedPythonDirectory).Wait(); @@ -207,4 +207,4 @@ namespace Flow.Launcher.Core.Plugin }); } } -} \ No newline at end of file +} From 7b50820765c87cd4fd7ba51354afca86673bc73f Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sat, 19 Jun 2021 21:04:34 +1000 Subject: [PATCH 288/398] add option to switch auto scrollbar hide on/off --- .../UserSettings/Settings.cs | 2 ++ Flow.Launcher/Languages/en.xaml | 2 ++ Flow.Launcher/SettingWindow.xaml | 11 ++++++++--- Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 12 ++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 76a370978..bd847bf28 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -97,6 +97,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool RememberLastLaunchLocation { get; set; } public bool IgnoreHotkeysOnFullscreen { get; set; } + public bool AutoHideScrollBar { get; set; } = false; + public HttpProxy Proxy { get; set; } = new HttpProxy(); [JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 6232492ba..bf34a842b 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -31,6 +31,8 @@ Ignore hotkeys in fullscreen mode Python Directory Auto Update + Auto Hide Scroll Bar + Automatically hides the setting window's scroll bar, and show when hover the mouse over it Select Hide Flow Launcher on startup Hide tray icon diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 4cb585597..a6a09a1be 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -36,7 +36,7 @@ - + @@ -63,9 +63,14 @@ - + + + + + Margin="10, 0, 10, 10" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ui:ScrollViewerHelper.AutoHideScrollBars="{Binding Settings.AutoHideScrollBar}"> diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 2108e2c40..05e170b09 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -61,6 +61,18 @@ namespace Flow.Launcher.ViewModel } } + public bool AutoHideScrollBar + { + get + { + return Settings.AutoHideScrollBar; + } + set + { + Settings.AutoHideScrollBar = value; + } + } + // This is only required to set at startup. When portable mode enabled/disabled a restart is always required private bool _portableMode = DataLocation.PortableDataLocationInUse(); public bool PortableMode From 3cc92c976f3d49ce904364d1c5d42d31e81fdfaa Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sat, 19 Jun 2021 21:12:12 +1000 Subject: [PATCH 289/398] update wording --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index bf34a842b..838bfc1d1 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -32,7 +32,7 @@ Python Directory Auto Update Auto Hide Scroll Bar - Automatically hides the setting window's scroll bar, and show when hover the mouse over it + Automatically hides the setting window's scroll bar, and show when hover the mouse over it. This setting will take effect on next window open Select Hide Flow Launcher on startup Hide tray icon From f561b1166f516b158f1501eae58485b24a66e7c3 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sat, 19 Jun 2021 21:38:35 +1000 Subject: [PATCH 290/398] update wording --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 838bfc1d1..ace13e5bb 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -32,7 +32,7 @@ Python Directory Auto Update Auto Hide Scroll Bar - Automatically hides the setting window's scroll bar, and show when hover the mouse over it. This setting will take effect on next window open + Automatically hides the Settings window scroll bar, and show when hover the mouse over it. This setting will take effect on next window open Select Hide Flow Launcher on startup Hide tray icon From a0011db9d3c3395116ab92853a28778aa92e302b Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sat, 19 Jun 2021 21:49:07 +1000 Subject: [PATCH 291/398] add auto hide scroll bar option to Theme tab --- Flow.Launcher/SettingWindow.xaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index a6a09a1be..a04b78667 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -221,8 +221,10 @@ - - Open Theme Folder + From 7319133ae8b9f0f8f7e88cd0712f6bb8c7123ef5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 21 Jun 2021 12:34:07 +1000 Subject: [PATCH 292/398] add backwards compatibility with Everything plugin --- Flow.Launcher.Core/Plugin/PluginManager.cs | 7 +- .../Storage/ISavable.cs | 11 +++ .../Storage/JsonStorage.cs | 90 +++++++++++++++++++ .../Storage/PluginJsonStorage.cs | 22 ++++- .../{ISavable.cs => ISettingsSavable.cs} | 2 +- Flow.Launcher/PublicAPIInstance.cs | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 2 +- 8 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 Flow.Launcher.Infrastructure/Storage/ISavable.cs rename Flow.Launcher.Plugin/Interfaces/{ISavable.cs => ISettingsSavable.cs} (91%) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 8d2c6df24..06f8cca19 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -48,9 +48,14 @@ namespace Flow.Launcher.Core.Plugin { foreach (var plugin in AllPlugins) { - var savable = plugin.Plugin as ISavable; + var savable = plugin.Plugin as ISettingsSavable; savable?.Save(); } + + // Everything plugin + var savableEverything = AllPlugins.FirstOrDefault(x => x.Metadata.ID == "D2D2C23B084D411DB66FE0C79D6C2A6E")?.Plugin as ISavable; + savableEverything?.Save(); + API.SavePluginSettings(); } diff --git a/Flow.Launcher.Infrastructure/Storage/ISavable.cs b/Flow.Launcher.Infrastructure/Storage/ISavable.cs new file mode 100644 index 000000000..155f1af61 --- /dev/null +++ b/Flow.Launcher.Infrastructure/Storage/ISavable.cs @@ -0,0 +1,11 @@ +namespace Flow.Launcher.Infrastructure.Storage +{ + /// + /// Save plugin settings/cache, + /// todo should be merged into a abstract class intead of seperate interface + /// + public interface ISavable + { + void Save(); + } +} \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 43d19bea8..6863b2b57 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -87,4 +87,94 @@ namespace Flow.Launcher.Infrastructure.Storage File.WriteAllText(FilePath, serialized); } } + + public class JsonStrorage where T : new() + { + private readonly JsonSerializerOptions _serializerSettings; + private T _data; + // need a new directory name + public const string DirectoryName = "Settings"; + public const string FileSuffix = ".json"; + public string FilePath { get; set; } + public string DirectoryPath { get; set; } + + + internal JsonStrorage() + { + // use property initialization instead of DefaultValueAttribute + // easier and flexible for default value of object + _serializerSettings = new JsonSerializerOptions + { + IgnoreNullValues = false + }; + } + + public T Load() + { + if (File.Exists(FilePath)) + { + var searlized = File.ReadAllText(FilePath); + if (!string.IsNullOrWhiteSpace(searlized)) + { + Deserialize(searlized); + } + else + { + LoadDefault(); + } + } + else + { + LoadDefault(); + } + return _data.NonNull(); + } + + private void Deserialize(string searlized) + { + try + { + _data = JsonSerializer.Deserialize(searlized, _serializerSettings); + } + catch (JsonException e) + { + LoadDefault(); + Log.Exception($"|JsonStrorage.Deserialize|Deserialize error for json <{FilePath}>", e); + } + + if (_data == null) + { + LoadDefault(); + } + } + + private void LoadDefault() + { + if (File.Exists(FilePath)) + { + BackupOriginFile(); + } + + _data = new T(); + Save(); + } + + private void BackupOriginFile() + { + var timestamp = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fffffff", CultureInfo.CurrentUICulture); + var directory = Path.GetDirectoryName(FilePath).NonNull(); + var originName = Path.GetFileNameWithoutExtension(FilePath); + var backupName = $"{originName}-{timestamp}{FileSuffix}"; + var backupPath = Path.Combine(directory, backupName); + File.Copy(FilePath, backupPath, true); + // todo give user notification for the backup process + } + + public void Save() + { + string serialized = JsonSerializer.Serialize(_data, new JsonSerializerOptions() { WriteIndented = true }); + + File.WriteAllText(FilePath, serialized); + } + } } diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index c26aef95d..585770e0f 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -3,9 +3,9 @@ using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher.Infrastructure.Storage { - public class PluginJsonStorage :JsonStorage where T : new() + public class PluginJsonSettingStorage :JsonStorage where T : new() { - public PluginJsonStorage() + public PluginJsonSettingStorage() { // C# related, add python related below var dataType = typeof(T); @@ -16,9 +16,25 @@ namespace Flow.Launcher.Infrastructure.Storage FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); } - public PluginJsonStorage(T data) : this() + public PluginJsonSettingStorage(T data) : this() { _data = data; } } + + public class PluginJsonStorage : JsonStrorage where T : new() + { + public PluginJsonStorage() + { + // C# releated, add python releated below + var dataType = typeof(T); + var assemblyName = typeof(T).Assembly.GetName().Name; + DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, assemblyName); + Helper.ValidateDirectory(DirectoryPath); + + FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); + } + } + } + diff --git a/Flow.Launcher.Plugin/Interfaces/ISavable.cs b/Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs similarity index 91% rename from Flow.Launcher.Plugin/Interfaces/ISavable.cs rename to Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs index 6f408dc2e..120a18adc 100644 --- a/Flow.Launcher.Plugin/Interfaces/ISavable.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs @@ -5,7 +5,7 @@ /// Otherwise if LoadSettingJsonStorage or SaveSettingJsonStorage has been callded, /// plugin settings will be automatically saved (see Flow.Launcher/PublicAPIInstance.SavePluginSettings) by Flow /// - public interface ISavable + public interface ISettingsSavable { void Save(); } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index f3403696e..6502e735a 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -177,7 +177,7 @@ namespace Flow.Launcher public void SaveJsonStorage(T settings) where T : new() { var type = typeof(T); - _pluginJsonStorages[type] = new PluginJsonStorage(settings); + _pluginJsonStorages[type] = new PluginJsonSettingStorage(settings); ((PluginJsonStorage) _pluginJsonStorages[type]).Save(); } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 4ee15b46d..469be42c1 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -23,7 +23,7 @@ using System.Threading.Channels; namespace Flow.Launcher.ViewModel { - public class MainViewModel : BaseModel, ISavable + public class MainViewModel : BaseModel, ISettingsSavable { #region Private Fields diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 5175970fd..59bd4809d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -13,7 +13,7 @@ using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program { - public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable + public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISettingsSavable, IAsyncReloadable { internal static Win32[] _win32s { get; set; } internal static UWP.Application[] _uwps { get; set; } From 77f8a8e27b2bd4c52126f4df188208c92cb90bf5 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Mon, 21 Jun 2021 13:39:54 +0800 Subject: [PATCH 293/398] Use direct property to allow calling onPropertyChanged Remove default value initialization --- .../UserSettings/Settings.cs | 2 +- Flow.Launcher/SettingWindow.xaml | 6 ++--- .../ViewModel/SettingWindowViewModel.cs | 22 +++++++------------ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index bd847bf28..e7e7902d4 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -97,7 +97,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool RememberLastLaunchLocation { get; set; } public bool IgnoreHotkeysOnFullscreen { get; set; } - public bool AutoHideScrollBar { get; set; } = false; + public bool AutoHideScrollBar { get; set; } public HttpProxy Proxy { get; set; } = new HttpProxy(); diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index a04b78667..ad24d189f 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -36,7 +36,7 @@ - + @@ -67,7 +67,7 @@ ToolTip="{DynamicResource ShouldUsePinyinToolTip}"> - @@ -117,7 +117,7 @@ + Margin="10, 0, 10, 10" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ui:ScrollViewerHelper.AutoHideScrollBars="{Binding AutoHideScrollBar, Mode=OneWay}"> diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 05e170b09..b974efd03 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -63,14 +63,8 @@ namespace Flow.Launcher.ViewModel public bool AutoHideScrollBar { - get - { - return Settings.AutoHideScrollBar; - } - set - { - Settings.AutoHideScrollBar = value; - } + get => Settings.AutoHideScrollBar; + set => Settings.AutoHideScrollBar = value; } // This is only required to set at startup. When portable mode enabled/disabled a restart is always required @@ -150,11 +144,11 @@ namespace Flow.Launcher.ViewModel public bool ShouldUsePinyin { - get + get { - return Settings.ShouldUsePinyin; + return Settings.ShouldUsePinyin; } - set + set { Settings.ShouldUsePinyin = value; } @@ -193,7 +187,7 @@ namespace Flow.Launcher.ViewModel } HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_updater.GitHubRepository); - + if (string.IsNullOrEmpty(proxyUserName) || string.IsNullOrEmpty(Settings.Proxy.Password)) { request.Proxy = new WebProxy(proxyServer, Settings.Proxy.Port); @@ -237,7 +231,7 @@ namespace Flow.Launcher.ViewModel var metadatas = PluginManager.AllPlugins .OrderBy(x => x.Metadata.Disabled) .ThenBy(y => y.Metadata.Name) - .Select(p => new PluginViewModel { PluginPair = p}) + .Select(p => new PluginViewModel { PluginPair = p }) .ToList(); return metadatas; } @@ -465,7 +459,7 @@ namespace Flow.Launcher.ViewModel #region about public string Website => Constant.Website; - public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest"; + public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest"; public string Documentation => Constant.Documentation; public static string Version => Constant.Version; public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes); From e74d7ce10245adc4cc9d9511a9e6b5b06d012dec Mon Sep 17 00:00:00 2001 From: pc223 <10551242+pc223@users.noreply.github.com> Date: Mon, 21 Jun 2021 15:26:06 +0700 Subject: [PATCH 294/398] Fix AmbiguousMatchException when plugins call Flow API (JsonRPC) Why: `GetMethod(String)` raise AmbiguousMatchException when method have overloads (e.g. `ShowMsg`) Solution: `Use GetMethod(String, Type[])` https://stackoverflow.com/questions/1969411/avoiding-an-ambiguous-match-exception --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index ff9c11a94..65f8fc608 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading; @@ -128,7 +129,8 @@ namespace Flow.Launcher.Core.Plugin private void ExecuteFlowLauncherAPI(string method, object[] parameters) { - MethodInfo methodInfo = PluginManager.API.GetType().GetMethod(method); + var parametersTypeArray = parameters.Select(param => param.GetType()).ToArray(); + MethodInfo methodInfo = PluginManager.API.GetType().GetMethod(method, parametersTypeArray); if (methodInfo != null) { try From 131c5cbfccf130d7910076c8673bba1c924a5754 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 21 Jun 2021 19:38:22 +1000 Subject: [PATCH 295/398] update binding for Theme tab --- Flow.Launcher/Languages/en.xaml | 2 +- Flow.Launcher/SettingWindow.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index ace13e5bb..573403823 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -32,7 +32,7 @@ Python Directory Auto Update Auto Hide Scroll Bar - Automatically hides the Settings window scroll bar, and show when hover the mouse over it. This setting will take effect on next window open + Automatically hides the Settings window scroll bar and show when hover the mouse over it Select Hide Flow Launcher on startup Hide tray icon diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index ad24d189f..d5ead386b 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -224,7 +224,7 @@ From 6ec151a8ab4f4ab93fdddf576c802da85995af20 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 21 Jun 2021 21:04:19 +1000 Subject: [PATCH 296/398] use inheritance for ISavable and JsonStrorage --- Flow.Launcher.Core/Plugin/PluginManager.cs | 4 - .../Storage/ISavable.cs | 9 +- .../Storage/JsonStorage.cs | 90 +------------------ 3 files changed, 5 insertions(+), 98 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 06f8cca19..b3a4ad0e1 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -52,10 +52,6 @@ namespace Flow.Launcher.Core.Plugin savable?.Save(); } - // Everything plugin - var savableEverything = AllPlugins.FirstOrDefault(x => x.Metadata.ID == "D2D2C23B084D411DB66FE0C79D6C2A6E")?.Plugin as ISavable; - savableEverything?.Save(); - API.SavePluginSettings(); } diff --git a/Flow.Launcher.Infrastructure/Storage/ISavable.cs b/Flow.Launcher.Infrastructure/Storage/ISavable.cs index 155f1af61..2fc577e86 100644 --- a/Flow.Launcher.Infrastructure/Storage/ISavable.cs +++ b/Flow.Launcher.Infrastructure/Storage/ISavable.cs @@ -1,11 +1,10 @@ -namespace Flow.Launcher.Infrastructure.Storage +using Flow.Launcher.Plugin; + +namespace Flow.Launcher.Infrastructure.Storage { /// /// Save plugin settings/cache, /// todo should be merged into a abstract class intead of seperate interface /// - public interface ISavable - { - void Save(); - } + public interface ISavable : ISettingsSavable { } } \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 6863b2b57..9a863f9fa 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -88,93 +88,5 @@ namespace Flow.Launcher.Infrastructure.Storage } } - public class JsonStrorage where T : new() - { - private readonly JsonSerializerOptions _serializerSettings; - private T _data; - // need a new directory name - public const string DirectoryName = "Settings"; - public const string FileSuffix = ".json"; - public string FilePath { get; set; } - public string DirectoryPath { get; set; } - - - internal JsonStrorage() - { - // use property initialization instead of DefaultValueAttribute - // easier and flexible for default value of object - _serializerSettings = new JsonSerializerOptions - { - IgnoreNullValues = false - }; - } - - public T Load() - { - if (File.Exists(FilePath)) - { - var searlized = File.ReadAllText(FilePath); - if (!string.IsNullOrWhiteSpace(searlized)) - { - Deserialize(searlized); - } - else - { - LoadDefault(); - } - } - else - { - LoadDefault(); - } - return _data.NonNull(); - } - - private void Deserialize(string searlized) - { - try - { - _data = JsonSerializer.Deserialize(searlized, _serializerSettings); - } - catch (JsonException e) - { - LoadDefault(); - Log.Exception($"|JsonStrorage.Deserialize|Deserialize error for json <{FilePath}>", e); - } - - if (_data == null) - { - LoadDefault(); - } - } - - private void LoadDefault() - { - if (File.Exists(FilePath)) - { - BackupOriginFile(); - } - - _data = new T(); - Save(); - } - - private void BackupOriginFile() - { - var timestamp = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fffffff", CultureInfo.CurrentUICulture); - var directory = Path.GetDirectoryName(FilePath).NonNull(); - var originName = Path.GetFileNameWithoutExtension(FilePath); - var backupName = $"{originName}-{timestamp}{FileSuffix}"; - var backupPath = Path.Combine(directory, backupName); - File.Copy(FilePath, backupPath, true); - // todo give user notification for the backup process - } - - public void Save() - { - string serialized = JsonSerializer.Serialize(_data, new JsonSerializerOptions() { WriteIndented = true }); - - File.WriteAllText(FilePath, serialized); - } - } + public class JsonStrorage : JsonStorage where T : new() { } } From 0387bfbb64cf206616f28d3f3dfb87154d9ff5b9 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 21 Jun 2021 21:31:07 +1000 Subject: [PATCH 297/398] update class name --- .../Storage/PluginJsonStorage.cs | 21 +++---------------- Flow.Launcher/PublicAPIInstance.cs | 2 +- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs index 585770e0f..923a1a6b5 100644 --- a/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs @@ -3,9 +3,9 @@ using Flow.Launcher.Infrastructure.UserSettings; namespace Flow.Launcher.Infrastructure.Storage { - public class PluginJsonSettingStorage :JsonStorage where T : new() + public class PluginJsonStorage :JsonStorage where T : new() { - public PluginJsonSettingStorage() + public PluginJsonStorage() { // C# related, add python related below var dataType = typeof(T); @@ -16,25 +16,10 @@ namespace Flow.Launcher.Infrastructure.Storage FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); } - public PluginJsonSettingStorage(T data) : this() + public PluginJsonStorage(T data) : this() { _data = data; } } - - public class PluginJsonStorage : JsonStrorage where T : new() - { - public PluginJsonStorage() - { - // C# releated, add python releated below - var dataType = typeof(T); - var assemblyName = typeof(T).Assembly.GetName().Name; - DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, assemblyName); - Helper.ValidateDirectory(DirectoryPath); - - FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}"); - } - } - } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 6502e735a..f3403696e 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -177,7 +177,7 @@ namespace Flow.Launcher public void SaveJsonStorage(T settings) where T : new() { var type = typeof(T); - _pluginJsonStorages[type] = new PluginJsonSettingStorage(settings); + _pluginJsonStorages[type] = new PluginJsonStorage(settings); ((PluginJsonStorage) _pluginJsonStorages[type]).Save(); } From 4b8b7a4f0df8f37336443c8d05d0f2706b62b054 Mon Sep 17 00:00:00 2001 From: pc223 <10551242+pc223@users.noreply.github.com> Date: Mon, 21 Jun 2021 18:56:05 +0700 Subject: [PATCH 298/398] [UI/UX] Remove bottom margin of General tab (Settings) The bottom margin (little gap) under General tab of Settings make it hard to "feel" the space is scrollable or not. New windows style and websites tend to not have margin in the bottom Related to: Issue #443 and PR #440 --- Flow.Launcher/SettingWindow.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index d5ead386b..0d966f1d6 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -36,7 +36,7 @@ - + From 644be7977cfcd89e8fc4fa2dfa1cc5bcbf4afacd Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 21 Jun 2021 21:56:20 +1000 Subject: [PATCH 299/398] use ISavable and add obsolete message --- Flow.Launcher.Core/Plugin/PluginManager.cs | 4 ++-- Flow.Launcher.Infrastructure/Storage/ISavable.cs | 10 ++++------ Flow.Launcher.Infrastructure/Storage/JsonStorage.cs | 2 ++ .../Interfaces/{ISettingsSavable.cs => ISavable.cs} | 2 +- Flow.Launcher/ViewModel/MainViewModel.cs | 3 ++- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) rename Flow.Launcher.Plugin/Interfaces/{ISettingsSavable.cs => ISavable.cs} (91%) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index b3a4ad0e1..59f34de74 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -7,9 +7,9 @@ using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; +using ISavable = Flow.Launcher.Plugin.ISavable; namespace Flow.Launcher.Core.Plugin { @@ -48,7 +48,7 @@ namespace Flow.Launcher.Core.Plugin { foreach (var plugin in AllPlugins) { - var savable = plugin.Plugin as ISettingsSavable; + var savable = plugin.Plugin as ISavable; savable?.Save(); } diff --git a/Flow.Launcher.Infrastructure/Storage/ISavable.cs b/Flow.Launcher.Infrastructure/Storage/ISavable.cs index 2fc577e86..ba2b58c6a 100644 --- a/Flow.Launcher.Infrastructure/Storage/ISavable.cs +++ b/Flow.Launcher.Infrastructure/Storage/ISavable.cs @@ -1,10 +1,8 @@ -using Flow.Launcher.Plugin; +using System; namespace Flow.Launcher.Infrastructure.Storage { - /// - /// Save plugin settings/cache, - /// todo should be merged into a abstract class intead of seperate interface - /// - public interface ISavable : ISettingsSavable { } + [Obsolete("Deprecated as of Flow Launcher v1.8.0, on 2021.06.21. " + + "This is used only for Everything plugin v1.4.9 or below backwards compatibility")] + public interface ISavable : Plugin.ISavable { } } \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 9a863f9fa..0083ccb87 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -88,5 +88,7 @@ namespace Flow.Launcher.Infrastructure.Storage } } + [Obsolete("Deprecated as of Flow Launcher v1.8.0, on 2021.06.21. " + + "This is used only for Everything plugin v1.4.9 or below backwards compatibility")] public class JsonStrorage : JsonStorage where T : new() { } } diff --git a/Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs b/Flow.Launcher.Plugin/Interfaces/ISavable.cs similarity index 91% rename from Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs rename to Flow.Launcher.Plugin/Interfaces/ISavable.cs index 120a18adc..6f408dc2e 100644 --- a/Flow.Launcher.Plugin/Interfaces/ISettingsSavable.cs +++ b/Flow.Launcher.Plugin/Interfaces/ISavable.cs @@ -5,7 +5,7 @@ /// Otherwise if LoadSettingJsonStorage or SaveSettingJsonStorage has been callded, /// plugin settings will be automatically saved (see Flow.Launcher/PublicAPIInstance.SavePluginSettings) by Flow /// - public interface ISettingsSavable + public interface ISavable { void Save(); } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 469be42c1..4f6d3be84 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -20,10 +20,11 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using Flow.Launcher.Infrastructure.Logger; using System.Threading.Channels; +using ISavable = Flow.Launcher.Plugin.ISavable; namespace Flow.Launcher.ViewModel { - public class MainViewModel : BaseModel, ISettingsSavable + public class MainViewModel : BaseModel, ISavable { #region Private Fields diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 59bd4809d..5175970fd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -13,7 +13,7 @@ using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program { - public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISettingsSavable, IAsyncReloadable + public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, ISavable, IAsyncReloadable { internal static Win32[] _win32s { get; set; } internal static UWP.Application[] _uwps { get; set; } From d3384bf67745d5d51a0019f6dd1409f0f5b885a6 Mon Sep 17 00:00:00 2001 From: pc223 <10551242+pc223@users.noreply.github.com> Date: Tue, 22 Jun 2021 17:46:01 +0700 Subject: [PATCH 300/398] [UI] Fix `Settings-window` doesn't show back up when minimized in taskbar by user Not sure why this works tho Maybe `.Show()` failed when `window.WindowState == Minimized` (not `Normal`) Not sure why need .Activate() too Ref: https://stackoverflow.com/a/59719760/4230390 --- Flow.Launcher/Helper/SingletonWindowOpener.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Flow.Launcher/Helper/SingletonWindowOpener.cs b/Flow.Launcher/Helper/SingletonWindowOpener.cs index 3671b9fd3..b969abcb4 100644 --- a/Flow.Launcher/Helper/SingletonWindowOpener.cs +++ b/Flow.Launcher/Helper/SingletonWindowOpener.cs @@ -11,7 +11,19 @@ namespace Flow.Launcher.Helper var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T)) ?? (T)Activator.CreateInstance(typeof(T), args); Application.Current.MainWindow.Hide(); + + + // Fix UI bug + // If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar + // Not sure why this works tho + // Probably because, when `.Show()` failed, `window.WindowState == Minimized` (not `Normal`) + // https://stackoverflow.com/a/59719760/4230390 + // Not sure why need .Activate() too + window.WindowState = WindowState.Normal; window.Show(); + window.Activate(); + + window.Focus(); return (T)window; From 43257ea9cf5655c6c11cdc70e2eec5cb8cb15b20 Mon Sep 17 00:00:00 2001 From: pc223 <10551242+pc223@users.noreply.github.com> Date: Tue, 22 Jun 2021 23:28:21 +0700 Subject: [PATCH 301/398] Remove .Activate() --- Flow.Launcher/Helper/SingletonWindowOpener.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/Helper/SingletonWindowOpener.cs b/Flow.Launcher/Helper/SingletonWindowOpener.cs index b969abcb4..3b2b3631d 100644 --- a/Flow.Launcher/Helper/SingletonWindowOpener.cs +++ b/Flow.Launcher/Helper/SingletonWindowOpener.cs @@ -14,14 +14,13 @@ namespace Flow.Launcher.Helper // Fix UI bug + // Add `window.WindowState = WindowState.Normal` // If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar // Not sure why this works tho - // Probably because, when `.Show()` failed, `window.WindowState == Minimized` (not `Normal`) + // Probably because, when `.Show()` fails, `window.WindowState == Minimized` (not `Normal`) // https://stackoverflow.com/a/59719760/4230390 - // Not sure why need .Activate() too window.WindowState = WindowState.Normal; window.Show(); - window.Activate(); window.Focus(); From 18b889c621c1a1bf24dd61381fa5aea96f7040b2 Mon Sep 17 00:00:00 2001 From: pc223 <10551242+pc223@users.noreply.github.com> Date: Wed, 23 Jun 2021 00:11:54 +0700 Subject: [PATCH 302/398] Remove empty lines --- Flow.Launcher/Helper/SingletonWindowOpener.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Flow.Launcher/Helper/SingletonWindowOpener.cs b/Flow.Launcher/Helper/SingletonWindowOpener.cs index 3b2b3631d..fdfaaa4fc 100644 --- a/Flow.Launcher/Helper/SingletonWindowOpener.cs +++ b/Flow.Launcher/Helper/SingletonWindowOpener.cs @@ -12,7 +12,6 @@ namespace Flow.Launcher.Helper ?? (T)Activator.CreateInstance(typeof(T), args); Application.Current.MainWindow.Hide(); - // Fix UI bug // Add `window.WindowState = WindowState.Normal` // If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar @@ -22,7 +21,6 @@ namespace Flow.Launcher.Helper window.WindowState = WindowState.Normal; window.Show(); - window.Focus(); return (T)window; From 1511f4b72531820bf3637a0d34419b10be87d424 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 24 Jun 2021 20:08:15 +0800 Subject: [PATCH 303/398] Release Unmanaged Object Co-Authored-By: Alekhya <28739210+alekhyareddy28@users.noreply.github.com> --- .../Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs index 4ded3412a..d17048dcb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs @@ -126,7 +126,12 @@ namespace Flow.Launcher.Plugin.Program.Programs ((IShellLinkW)link).GetDescription(buffer, MAX_PATH); description = buffer.ToString(); } + + // To release unmanaged memory + Marshal.ReleaseComObject(link); + return target; + } } } \ No newline at end of file From 794911aad4c29836aa19ee1dd60533d26ef42acf Mon Sep 17 00:00:00 2001 From: Zelin Liao Date: Sat, 26 Jun 2021 10:52:44 +0800 Subject: [PATCH 304/398] [Plugin.Shell] Fix: The Query Box Is Not Focused When HotKey Invoked --- Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 829398a4b..fe6726d57 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -298,7 +298,12 @@ namespace Flow.Launcher.Plugin.Shell private void OnWinRPressed() { context.API.ChangeQuery($"{context.CurrentPluginMetadata.ActionKeywords[0]}{Plugin.Query.TermSeperater}"); - Application.Current.MainWindow.Visibility = Visibility.Visible; + + // show the main window and set focus to the query box + Window mainWindow = Application.Current.MainWindow; + mainWindow.Visibility = Visibility.Visible; + mainWindow.Activate(); + mainWindow.Focus(); } public Control CreateSettingPanel() From 64cc4a9a6b9ee748e5df6c5b9f3307a010fb8724 Mon Sep 17 00:00:00 2001 From: pc223 <10551242+pc223@users.noreply.github.com> Date: Sat, 26 Jun 2021 15:42:55 +0700 Subject: [PATCH 305/398] Fix ContextMenu bug Should not switch to ContextMenu if SelectedItem == null --- Flow.Launcher/ViewModel/MainViewModel.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 4f6d3be84..ee36e11d8 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -211,7 +211,10 @@ namespace Flow.Launcher.ViewModel { if (SelectedIsFromQueryResults()) { - SelectedResults = ContextMenu; + // When switch to ContextMenu from QueryResults, but no item being chosen, should do nothing + // i.e. Shift+Enter/Ctrl+O right after Alt + Space should do nothing + if (SelectedResults.SelectedItem != null) + SelectedResults = ContextMenu; } else { From 867c2a711b3486f09e621e41e7cab3a8245fa69f Mon Sep 17 00:00:00 2001 From: Zelin Liao Date: Sat, 26 Jun 2021 20:42:09 +0800 Subject: [PATCH 306/398] Remove Uncessary Window.Activate() --- Flow.Launcher/CustomQueryHotkeySetting.xaml.cs | 11 +++++++++-- Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index d036c008c..813c5e254 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -55,7 +55,7 @@ namespace Flow.Launcher SetHotkey(ctlHotkey.CurrentHotkey, delegate { App.API.ChangeQuery(pluginHotkey.ActionKeyword); - Application.Current.MainWindow.Visibility = Visibility.Visible; + ShowMainWindow(); }); } else @@ -73,11 +73,18 @@ namespace Flow.Launcher SetHotkey(new HotkeyModel(updateCustomHotkey.Hotkey), delegate { App.API.ChangeQuery(updateCustomHotkey.ActionKeyword); - Application.Current.MainWindow.Visibility = Visibility.Visible; + ShowMainWindow(); }); } Close(); + + static void ShowMainWindow() + { + Window mainWindow = Application.Current.MainWindow; + mainWindow.Visibility = Visibility.Visible; + mainWindow.Focus(); + } } public void UpdateItem(CustomPluginHotkey item) diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index fe6726d57..58f8538f0 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -302,7 +302,6 @@ namespace Flow.Launcher.Plugin.Shell // show the main window and set focus to the query box Window mainWindow = Application.Current.MainWindow; mainWindow.Visibility = Visibility.Visible; - mainWindow.Activate(); mainWindow.Focus(); } From c8961a8ba815a2bc8dfffa2470f54e5ce0f50cf6 Mon Sep 17 00:00:00 2001 From: pc223 <10551242+pc223@users.noreply.github.com> Date: Sun, 27 Jun 2021 12:33:19 +0700 Subject: [PATCH 307/398] Remove top margin of General tab (Settings) Why: for similar UX to other apps/website --- Flow.Launcher/SettingWindow.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 0d966f1d6..17ece4755 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -36,8 +36,8 @@ - - + + From 6a6d6b22ee665a35d869e5fd29726e2eeceff5cc Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 27 Jun 2021 21:42:37 +1000 Subject: [PATCH 308/398] auto disable shadow when blur is enabled --- Flow.Launcher.Core/Resource/Theme.cs | 43 ++++++++++--------- Flow.Launcher/MainWindow.xaml.cs | 3 +- Flow.Launcher/SettingWindow.xaml | 2 +- .../ViewModel/SettingWindowViewModel.cs | 12 +++++- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index fe64e0c3e..e7a19d6a4 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -28,6 +28,8 @@ namespace Flow.Launcher.Core.Resource private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder); private string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder); + public bool BlurEnabled { get; set; } + public Theme() { _themeDirectories.Add(DirectoryPath); @@ -88,7 +90,9 @@ namespace Flow.Launcher.Core.Resource _oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath); } - if (Settings.UseDropShadowEffect) + BlurEnabled = IsBlurTheme(); + + if (Settings.UseDropShadowEffect && !BlurEnabled) AddDropShadowEffectToCurrentTheme(); } catch (DirectoryNotFoundException e) @@ -252,7 +256,7 @@ namespace Flow.Launcher.Core.Resource UpdateResourceDictionary(dict); } - public void RemoveDropShadowEffectToCurrentTheme() + public void RemoveDropShadowEffectFromCurrentTheme() { var dict = CurrentThemeResourceDictionary(); var windowBorderStyle = dict["WindowBorderStyle"] as Style; @@ -320,30 +324,29 @@ namespace Flow.Launcher.Core.Resource /// public void SetBlurForWindow() { + if (BlurEnabled) + { + SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_ENABLE_BLURBEHIND); + } + else + { + SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_DISABLED); + } + } - // Exception of FindResource can't be cathed if global exception handle is set + private bool IsBlurTheme() + { if (Environment.OSVersion.Version >= new Version(6, 2)) { var resource = Application.Current.TryFindResource("ThemeBlurEnabled"); - bool blur; - if (resource is bool) - { - blur = (bool)resource; - } - else - { - blur = false; - } - if (blur) - { - SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_ENABLE_BLURBEHIND); - } - else - { - SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_DISABLED); - } + if (resource is bool) + return (bool)resource; + + return false; } + + return false; } private void SetWindowAccent(Window w, AccentState state) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 4355cda15..177953912 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -61,7 +61,8 @@ namespace Flow.Launcher // show notify icon when flowlauncher is hidden InitializeNotifyIcon(); - // todo is there a way to set blur only once? + // currently blur can only be called when loading main window. + // is there a way to set blur only once? ThemeManager.Instance.SetBlurForWindow(); WindowsInteropHelper.DisableControlBox(this); InitProgressbarAnimation(); diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 0d966f1d6..1a8a12100 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -242,7 +242,7 @@ - + diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index b974efd03..b476599b5 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -271,6 +271,10 @@ namespace Flow.Launcher.ViewModel { Settings.Theme = value; ThemeManager.Instance.ChangeTheme(value); + + if (ThemeManager.Instance.BlurEnabled && Settings.UseDropShadowEffect) + DropShadowEffect = false; + } } @@ -282,13 +286,19 @@ namespace Flow.Launcher.ViewModel get { return Settings.UseDropShadowEffect; } set { + if (ThemeManager.Instance.BlurEnabled && value) + { + MessageBox.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + return; + } + if (value) { ThemeManager.Instance.AddDropShadowEffectToCurrentTheme(); } else { - ThemeManager.Instance.RemoveDropShadowEffectToCurrentTheme(); + ThemeManager.Instance.RemoveDropShadowEffectFromCurrentTheme(); } Settings.UseDropShadowEffect = value; From 52fbebf2d6881a0521c95d0a61bd95874981a3b4 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Sun, 27 Jun 2021 22:13:58 +1000 Subject: [PATCH 309/398] prompt user restart required when blur theme is selected first time --- Flow.Launcher.Core/Resource/Theme.cs | 4 ++++ Flow.Launcher/Languages/en.xaml | 2 ++ Flow.Launcher/MainWindow.xaml.cs | 2 ++ Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 7 ++++++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index e7a19d6a4..00e62ad77 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -30,6 +30,10 @@ namespace Flow.Launcher.Core.Resource public bool BlurEnabled { get; set; } + // due to blur only enabled when main window is loaded for the first time, this is used to track the blur status + // when the window is first loaded and prompt user that a restart is required for blur to take effect. + public bool BlurEnabledOnloaded { get; set; } + public Theme() { _themeDirectories.Add(DirectoryPath); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 573403823..f04677669 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -39,6 +39,8 @@ Query Search Precision Should Use Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for transliterating Chinese + Shadow effect is not allowed while current theme has blur effect enabled + Current theme has blur effect enabled for the first time, a restart is required in order to take effect Plugin diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 177953912..f712f9a6e 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -64,6 +64,8 @@ namespace Flow.Launcher // currently blur can only be called when loading main window. // is there a way to set blur only once? ThemeManager.Instance.SetBlurForWindow(); + ThemeManager.Instance.BlurEnabledOnloaded = ThemeManager.Instance.BlurEnabled; + WindowsInteropHelper.DisableControlBox(this); InitProgressbarAnimation(); InitializePosition(); diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index b476599b5..c3c35f256 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -275,6 +275,11 @@ namespace Flow.Launcher.ViewModel if (ThemeManager.Instance.BlurEnabled && Settings.UseDropShadowEffect) DropShadowEffect = false; + if (!ThemeManager.Instance.BlurEnabledOnloaded && ThemeManager.Instance.BlurEnabled) + { + MessageBox.Show(InternationalizationManager.Instance.GetTranslation("blurEffectRequireRestart")); + ThemeManager.Instance.BlurEnabledOnloaded = true; + } } } @@ -288,7 +293,7 @@ namespace Flow.Launcher.ViewModel { if (ThemeManager.Instance.BlurEnabled && value) { - MessageBox.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem")); + MessageBox.Show(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed")); return; } From e2b983df5f5f8d09869e6ee735b524313d6be04a Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 28 Jun 2021 07:01:56 +1000 Subject: [PATCH 310/398] version bump Shell plugin --- Plugins/Flow.Launcher.Plugin.Shell/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json index d59f8a30f..bccba4590 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Shell/plugin.json @@ -4,7 +4,7 @@ "Name": "Shell", "Description": "Provide executing commands from Flow Launcher", "Author": "qianlifeng", - "Version": "1.4.2", + "Version": "1.4.3", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Shell.dll", From b729cb1c7de5b10814867381733010dc59381776 Mon Sep 17 00:00:00 2001 From: Kevin Zhang Date: Thu, 24 Jun 2021 12:38:18 +0800 Subject: [PATCH 311/398] remove createdump.exe --- Scripts/post_build.ps1 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index ce9944624..8a03d6739 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -47,10 +47,21 @@ function Delete-Unused ($path, $config) { Remove-Item -Path $target -Include "*.xml" -Recurse } +function Remove-CreateDumpExe ($path, $config) { + $target = "$path\Output\$config" + + $depjson = Get-Content $target\Flow.Launcher.deps.json -raw |ConvertFrom-Json -depth 32 + $depjson.targets.'.NETCoreApp,Version=v5.0/win-x64'.'runtimepack.Microsoft.NETCore.App.Runtime.win-x64/5.0.6'.native.PSObject.Properties.Remove("createdump.exe") + $depjson|ConvertTo-Json -Depth 32|Out-File $target\Flow.Launcher.deps.json + Remove-Item -Path $target -Include "*createdump.exe" -Recurse +} + + function Validate-Directory ($output) { New-Item $output -ItemType Directory -Force } + function Pack-Squirrel-Installer ($path, $version, $output) { # msbuild based installer generation is not working in appveyor, not sure why Write-Host "Begin pack squirrel installer" @@ -108,6 +119,8 @@ function Main { Publish-Self-Contained $p + Remove-CreateDumpExe $p $config + $o = "$p\Output\Packages" Validate-Directory $o Pack-Squirrel-Installer $p $v $o From 1da58769e0b4cc5169fa6326c21239396020197a Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 28 Jun 2021 12:28:48 +1000 Subject: [PATCH 312/398] Update README.md --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ed85d7616..18680c2da 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,21 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be Remember to star it, flow will love you more :) +--- + +

+ Features • + Getting Started • + Plugins • + Status • + Contributing • + Question/Suggestion • + Developing/Debugging • + Documentation +

+ +--- + ## Features ![The Flow](https://user-images.githubusercontent.com/26427004/82151677-fa9c7100-989f-11ea-9143-81de60aaf07d.gif) @@ -33,7 +48,9 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be [ **SOFTPEDIA EDITOR'S PICK**](https://www.softpedia.com/get/System/Launchers-Shutdown-Tools/Flow-Launcher.shtml) -## Running Flow Launcher +## Getting Started + +### Installation | [Windows 7 and up installer](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) | `WinGet install "Flow Launcher"` | | -------------------------------------------------------------------------------------------- | -------------------------------- | From db70a09e90182b45f58d2e374e4ee0bbdc3e36ca Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 28 Jun 2021 12:32:29 +1000 Subject: [PATCH 313/398] fix links --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 18680c2da..6a9ebc0f1 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,14 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be ---

- Features • - Getting Started • - Plugins • - Status • - Contributing • - Question/Suggestion • - Developing/Debugging • - Documentation + Features • + Getting Started • + Plugins • + Status • + Contributing • + Question/Suggestion • + Developing/Debugging • + Documentation

--- From 46d8508646c46262372795d675528d8c33c43502 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 28 Jun 2021 12:36:25 +1000 Subject: [PATCH 314/398] remove slashes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6a9ebc0f1..a790ae597 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be PluginsStatusContributing • - Question/Suggestion • - Developing/Debugging • + Question/Suggestion • + Developing/DebuggingDocumentation From cc9c51d8998bcaf16d67d01e78a19494f7acfc92 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Mon, 28 Jun 2021 11:03:09 +0800 Subject: [PATCH 315/398] Use Regex instead of parsing JsonObject --- Scripts/post_build.ps1 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 8a03d6739..7f089a440 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -50,10 +50,8 @@ function Delete-Unused ($path, $config) { function Remove-CreateDumpExe ($path, $config) { $target = "$path\Output\$config" - $depjson = Get-Content $target\Flow.Launcher.deps.json -raw |ConvertFrom-Json -depth 32 - $depjson.targets.'.NETCoreApp,Version=v5.0/win-x64'.'runtimepack.Microsoft.NETCore.App.Runtime.win-x64/5.0.6'.native.PSObject.Properties.Remove("createdump.exe") - $depjson|ConvertTo-Json -Depth 32|Out-File $target\Flow.Launcher.deps.json - Remove-Item -Path $target -Include "*createdump.exe" -Recurse + $depjson = Get-Content $target\Flow.Launcher.deps.json -raw + $depjson -replace '(?s)(.createdump.exe": {.*?}.*?\n)\s*', "" | Out-File $target\Flow.Launcher.deps.json } @@ -127,4 +125,4 @@ function Main { } } -Main \ No newline at end of file +Main From 4443200490f986e959c5dd82ca8ba690b1a4caa7 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Mon, 28 Jun 2021 11:26:18 +0800 Subject: [PATCH 316/398] remove createdump.exe --- Scripts/post_build.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 7f089a440..cfa438e10 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -52,6 +52,7 @@ function Remove-CreateDumpExe ($path, $config) { $depjson = Get-Content $target\Flow.Launcher.deps.json -raw $depjson -replace '(?s)(.createdump.exe": {.*?}.*?\n)\s*', "" | Out-File $target\Flow.Launcher.deps.json + Remove-Item -Path $target -Include "*createdump.exe" -Recurse } From 5442a066b86fc8455b4fe9870cc65fead0e78785 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Mon, 28 Jun 2021 12:16:59 +0800 Subject: [PATCH 317/398] Use UTF8 --- Scripts/post_build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index cfa438e10..093f92768 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -51,7 +51,7 @@ function Remove-CreateDumpExe ($path, $config) { $target = "$path\Output\$config" $depjson = Get-Content $target\Flow.Launcher.deps.json -raw - $depjson -replace '(?s)(.createdump.exe": {.*?}.*?\n)\s*', "" | Out-File $target\Flow.Launcher.deps.json + $depjson -replace '(?s)(.createdump.exe": {.*?}.*?\n)\s*', "" | Out-File $target\Flow.Launcher.deps.json -Encoding UTF8 Remove-Item -Path $target -Include "*createdump.exe" -Recurse } From aa34a0202d620e61daf22548425c693320867900 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 29 Jun 2021 07:00:01 +1000 Subject: [PATCH 318/398] set blur when theme change is called --- Flow.Launcher.Core/Resource/Theme.cs | 8 ++++---- Flow.Launcher/Languages/en.xaml | 1 - Flow.Launcher/MainWindow.xaml.cs | 5 ----- Flow.Launcher/ViewModel/SettingWindowViewModel.cs | 6 ------ 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 00e62ad77..488e73eb6 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -30,10 +30,6 @@ namespace Flow.Launcher.Core.Resource public bool BlurEnabled { get; set; } - // due to blur only enabled when main window is loaded for the first time, this is used to track the blur status - // when the window is first loaded and prompt user that a restart is required for blur to take effect. - public bool BlurEnabledOnloaded { get; set; } - public Theme() { _themeDirectories.Add(DirectoryPath); @@ -98,6 +94,8 @@ namespace Flow.Launcher.Core.Resource if (Settings.UseDropShadowEffect && !BlurEnabled) AddDropShadowEffectToCurrentTheme(); + + SetBlurForWindow(); } catch (DirectoryNotFoundException e) { @@ -356,6 +354,8 @@ namespace Flow.Launcher.Core.Resource private void SetWindowAccent(Window w, AccentState state) { var windowHelper = new WindowInteropHelper(w); + w.Width = 750; + windowHelper.EnsureHandle(); var accent = new AccentPolicy { AccentState = state }; var accentStructSize = Marshal.SizeOf(accent); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index f04677669..efb82b478 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -40,7 +40,6 @@ Should Use Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for transliterating Chinese Shadow effect is not allowed while current theme has blur effect enabled - Current theme has blur effect enabled for the first time, a restart is required in order to take effect Plugin diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index f712f9a6e..735103938 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -61,11 +61,6 @@ namespace Flow.Launcher // show notify icon when flowlauncher is hidden InitializeNotifyIcon(); - // currently blur can only be called when loading main window. - // is there a way to set blur only once? - ThemeManager.Instance.SetBlurForWindow(); - ThemeManager.Instance.BlurEnabledOnloaded = ThemeManager.Instance.BlurEnabled; - WindowsInteropHelper.DisableControlBox(this); InitProgressbarAnimation(); InitializePosition(); diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index c3c35f256..53789d2d9 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -274,12 +274,6 @@ namespace Flow.Launcher.ViewModel if (ThemeManager.Instance.BlurEnabled && Settings.UseDropShadowEffect) DropShadowEffect = false; - - if (!ThemeManager.Instance.BlurEnabledOnloaded && ThemeManager.Instance.BlurEnabled) - { - MessageBox.Show(InternationalizationManager.Instance.GetTranslation("blurEffectRequireRestart")); - ThemeManager.Instance.BlurEnabledOnloaded = true; - } } } From b5272ddb4707e86c54c94b8f6b754c145dfed56f Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 29 Jun 2021 09:21:53 +1000 Subject: [PATCH 319/398] allow main query window's width to be dynamic --- Flow.Launcher.Core/Resource/Theme.cs | 22 +++++++++++++++++++++- Flow.Launcher/MainWindow.xaml | 2 +- Flow.Launcher/Themes/Base.xaml | 2 ++ Flow.Launcher/Themes/BlurBlack Darker.xaml | 1 - Flow.Launcher/Themes/BlurBlack.xaml | 1 - Flow.Launcher/Themes/BlurWhite.xaml | 1 - 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 488e73eb6..e70de673e 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -30,6 +30,8 @@ namespace Flow.Launcher.Core.Resource public bool BlurEnabled { get; set; } + private double mainWindowWidth; + public Theme() { _themeDirectories.Add(DirectoryPath); @@ -187,6 +189,21 @@ namespace Flow.Launcher.Core.Resource Array.ForEach(new[] { resultItemStyle, resultSubItemStyle, resultItemSelectedStyle, resultSubItemSelectedStyle }, o => Array.ForEach(setters, p => o.Setters.Add(p))); } + var windowStyle = dict["WindowStyle"] as Style; + + var width = windowStyle?.Setters.OfType().Where(x => x.Property.Name == "Width") + .Select(x => x.Value).FirstOrDefault(); + + if (width == null) + { + windowStyle = dict["BaseWindowStyle"] as Style; + + width = windowStyle?.Setters.OfType().Where(x => x.Property.Name == "Width") + .Select(x => x.Value).FirstOrDefault(); + } + + mainWindowWidth = (double)width; + return dict; } @@ -354,8 +371,11 @@ namespace Flow.Launcher.Core.Resource private void SetWindowAccent(Window w, AccentState state) { var windowHelper = new WindowInteropHelper(w); - w.Width = 750; + + // this determines the width of the main query window + w.Width = mainWindowWidth; windowHelper.EnsureHandle(); + var accent = new AccentPolicy { AccentState = state }; var accentStructSize = Marshal.SizeOf(accent); diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index aa0240dd4..7d0632d70 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -60,7 +60,7 @@ - + diff --git a/Flow.Launcher/Themes/Base.xaml b/Flow.Launcher/Themes/Base.xaml index eb2af490a..df0304bc2 100644 --- a/Flow.Launcher/Themes/Base.xaml +++ b/Flow.Launcher/Themes/Base.xaml @@ -46,6 +46,8 @@
/// Add ActionKeyword for specific plugin diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 200eb166a..6c55e37fe 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -117,7 +117,7 @@ namespace Flow.Launcher public Task HttpGetStreamAsync(string url, CancellationToken token = default) => Http.GetStreamAsync(url); - public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath) => Http.DownloadAsync(url, filePath); + public Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, CancellationToken token = default) => Http.DownloadAsync(url, filePath, token); public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword); From 14e51c07310fd38d5e0a93964bf04a4c77ab9695 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 06:42:18 +1100 Subject: [PATCH 045/398] update comments --- Flow.Launcher.Plugin/IPublicAPI.cs | 23 ++++++++++++----------- Flow.Launcher/PublicAPIInstance.cs | 1 - 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index 27a65191a..07e2b2700 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -90,15 +90,15 @@ namespace Flow.Launcher.Plugin event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; /// - /// Fuzzy Search the string with query + /// Fuzzy Search the string with the given query. This is the core search mechanism Flow uses /// - /// Query String - /// The string to Search for Query + /// Query string + /// The string that will be compared against the query /// Match results MatchResult FuzzySearch(string query, string stringToCompare); /// - /// Http Get to the spefic URL + /// Http download the spefic url and return as string /// /// URL to call Http Get /// Cancellation Token @@ -106,7 +106,7 @@ namespace Flow.Launcher.Plugin Task HttpGetStringAsync(string url, CancellationToken token = default); /// - /// Http Get to the spefic URL + /// Http download the spefic url and return as stream /// /// URL to call Http Get /// Cancellation Token @@ -136,35 +136,36 @@ namespace Flow.Launcher.Plugin void RemoveActionKeyword(string pluginId, string oldActionKeyword); /// - /// Log Debug message + /// Log debug message /// Message will only be logged in Debug mode /// void LogDebug(string className, string message, [CallerMemberName] string methodName = ""); /// - /// Log Message + /// Log info message /// void LogInfo(string className, string message, [CallerMemberName] string methodName = ""); /// - /// Log Warning + /// Log warning message /// void LogWarn(string className, string message, [CallerMemberName] string methodName = ""); /// - /// Log an Exception + /// Log an Exception. Will throw if in debug mode so developer will be aware, + /// otherwise logs the eror message. This is the primary logging method used for Flow /// void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = ""); /// - /// Load JsonStorage for current plugin + /// Load JsonStorage for current plugin. This is the method used to load settings from json in Flow /// /// Type for deserialization /// T LoadJsonStorage() where T : new(); /// - /// Save JsonStorage for current plugin + /// Save JsonStorage for current plugin. This is the method used to save settings to json in Flow /// /// Type for Serialization /// diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 6c55e37fe..9b41c2c05 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -150,7 +150,6 @@ namespace Flow.Launcher return true; } - #endregion } } From 2b6b5e9d8bdae77b5ef8be2feb0abf47e5a7bf46 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 07:05:46 +1100 Subject: [PATCH 046/398] version bump --- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index f713a33ec..93e5079c2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.4.0", + "Version": "1.4.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", From ba5cb5788e61b8e6eefbef21fa12a32f40368fd0 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 07:18:16 +1100 Subject: [PATCH 047/398] remove unused variables --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 1 - Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 22f4aea59..bd09cd9c6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -228,7 +228,6 @@ namespace Flow.Launcher.Plugin.Program public static void StartProcess(Func runProcess, ProcessStartInfo info) { - bool hide; try { runProcess(info); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index d73483818..20df233bb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -33,7 +33,6 @@ namespace Flow.Launcher.Plugin.Program.Programs public string Location => ParentDirectory; private const string ShortcutExtension = "lnk"; - private const string ApplicationReferenceExtension = "appref-ms"; private const string ExeExtension = "exe"; From 22bdbc93675df9485a8e4e9bad3f6c281d56a90c Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 08:19:48 +1100 Subject: [PATCH 048/398] add match on path --- .../Programs/Win32.cs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 20df233bb..3c0d1954c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -41,36 +41,38 @@ namespace Flow.Launcher.Plugin.Program.Programs string title; MatchResult matchResult; + var nameMatchResult = StringMatcher.FuzzySearch(query, Name); + var descriptionMatchResult = StringMatcher.FuzzySearch(query, Description); + var pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); + + var score = new[] { nameMatchResult.Score, descriptionMatchResult.Score, pathMatchResult.Score }.Max(); + + if (score < (int)StringMatcher.Instance.UserSettingSearchPrecision) + return null; + // We suppose Name won't be null - if (Description == null || Name.StartsWith(Description)) + if (score == nameMatchResult.Score && (Description == null || Name.StartsWith(Description))) { title = Name; - matchResult = StringMatcher.FuzzySearch(query, title); + matchResult = nameMatchResult; } - else if (Description.StartsWith(Name)) + else if (score == descriptionMatchResult.Score && Description.StartsWith(Name)) { title = Description; - matchResult = StringMatcher.FuzzySearch(query, Description); + + for (int i = 0; i < descriptionMatchResult.MatchData.Count; i++) + { + descriptionMatchResult.MatchData[i] += Name.Length + 2; // 2 is ": " + } + + matchResult = descriptionMatchResult; } else { title = $"{Name}: {Description}"; - var nameMatch = StringMatcher.FuzzySearch(query, Name); - var desciptionMatch = StringMatcher.FuzzySearch(query, Description); - if (desciptionMatch.Score > nameMatch.Score) - { - for (int i = 0; i < desciptionMatch.MatchData.Count; i++) - { - desciptionMatch.MatchData[i] += Name.Length + 2; // 2 is ": " - } - matchResult = desciptionMatch; - } - else matchResult = nameMatch; + matchResult = pathMatchResult; } - if (!matchResult.Success) return null; - - var result = new Result { Title = title, From ad998b17886d0b4553eb53364ce86bec0172ce07 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 14:01:07 +1100 Subject: [PATCH 049/398] add only path string match when lnk programs --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 3c0d1954c..74b85fd46 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -43,7 +43,10 @@ namespace Flow.Launcher.Plugin.Program.Programs var nameMatchResult = StringMatcher.FuzzySearch(query, Name); var descriptionMatchResult = StringMatcher.FuzzySearch(query, Description); - var pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); + + var pathMatchResult = new MatchResult(false, 0, new List(), 0); + if ((LnkResolvedPath ?? FullPath) != FullPath) + pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); var score = new[] { nameMatchResult.Score, descriptionMatchResult.Score, pathMatchResult.Score }.Max(); From 79d195f7e656dbf38fd5383fccf3cf0ecc97622e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 11:49:18 +0800 Subject: [PATCH 050/398] Change logic --- .../Programs/Win32.cs | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 74b85fd46..275726595 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -39,41 +39,52 @@ namespace Flow.Launcher.Plugin.Program.Programs public Result Result(string query, IPublicAPI api) { string title; - MatchResult matchResult; var nameMatchResult = StringMatcher.FuzzySearch(query, Name); var descriptionMatchResult = StringMatcher.FuzzySearch(query, Description); var pathMatchResult = new MatchResult(false, 0, new List(), 0); - if ((LnkResolvedPath ?? FullPath) != FullPath) + if (ExecutableName != null) // only lnk program will need this one pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); - var score = new[] { nameMatchResult.Score, descriptionMatchResult.Score, pathMatchResult.Score }.Max(); + MatchResult matchResult = nameMatchResult; - if (score < (int)StringMatcher.Instance.UserSettingSearchPrecision) - return null; + if (nameMatchResult.Score < descriptionMatchResult.Score) + matchResult = descriptionMatchResult; + + if (!matchResult.IsSearchPrecisionScoreMet()) + { + if (pathMatchResult.IsSearchPrecisionScoreMet()) + matchResult = pathMatchResult; + else return null; + } // We suppose Name won't be null - if (score == nameMatchResult.Score && (Description == null || Name.StartsWith(Description))) + if (Description == null || Name.StartsWith(Description)) { title = Name; - matchResult = nameMatchResult; } - else if (score == descriptionMatchResult.Score && Description.StartsWith(Name)) + else if (Description.StartsWith(Name)) { title = Description; - - for (int i = 0; i < descriptionMatchResult.MatchData.Count; i++) - { - descriptionMatchResult.MatchData[i] += Name.Length + 2; // 2 is ": " - } - - matchResult = descriptionMatchResult; } else { title = $"{Name}: {Description}"; - matchResult = pathMatchResult; + + if (matchResult == descriptionMatchResult) + { + for (int i = 0; i < descriptionMatchResult.MatchData.Count; i++) + { + matchResult.MatchData[i] += Name.Length + 2; // 2 is ": " + } + } + } + + if (matchResult == pathMatchResult) + { + // path Match won't have valid highlight data + matchResult.MatchData = new List(); } var result = new Result @@ -175,7 +186,7 @@ namespace Flow.Launcher.Plugin.Program.Programs public override string ToString() { - return ExecutableName; + return Name; } private static Win32 Win32Program(string path) @@ -200,7 +211,7 @@ namespace Flow.Launcher.Plugin.Program.Programs ProgramLogger.LogException($"|Win32|Win32Program|{path}" + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() {Valid = false, Enabled = false}; + return new Win32() { Valid = false, Enabled = false }; } } @@ -278,7 +289,7 @@ namespace Flow.Launcher.Plugin.Program.Programs ProgramLogger.LogException($"|Win32|ExeProgram|{path}" + $"|Permission denied when trying to load the program from {path}", e); - return new Win32() {Valid = false, Enabled = false}; + return new Win32() { Valid = false, Enabled = false }; } } @@ -420,7 +431,6 @@ namespace Flow.Launcher.Plugin.Program.Programs return null; var entry = Win32Program(path); - entry.ExecutableName = Path.GetFileName(path); return entry; } @@ -450,7 +460,7 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - public static IEnumerable DistinctBy(IEnumerable source, Func selector) + public static IEnumerable DistinctBy(IEnumerable source, Func selector) { var set = new HashSet(); foreach (var item in source) From 208bde0f99539c2cc40ce6473e364db53196497f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 11:50:27 +0800 Subject: [PATCH 051/398] Use ExecutableName instead of getting it from path --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 275726595..372d36524 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -45,7 +45,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var pathMatchResult = new MatchResult(false, 0, new List(), 0); if (ExecutableName != null) // only lnk program will need this one - pathMatchResult = StringMatcher.FuzzySearch(query, Path.GetFileNameWithoutExtension(FullPath)); + pathMatchResult = StringMatcher.FuzzySearch(query, ExecutableName); MatchResult matchResult = nameMatchResult; From 7e4ac1ce8f74f6809feedf4aa2fd0af6a084cad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 11:58:33 +0800 Subject: [PATCH 052/398] fix formatting --- .../Programs/ApplicationActivationHelper.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs index 3023836c1..4b8423ed6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs @@ -21,9 +21,9 @@ namespace Flow.Launcher.Plugin.Program.Programs [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IApplicationActivationManager { - IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); - IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); - IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); + IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); + IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); } // Application Activation Manager Class @@ -32,13 +32,13 @@ namespace Flow.Launcher.Plugin.Program.Programs { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] - public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId); + public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId); + public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId); + public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); } } } From 4b4953e3ac78c17f94a9bc09f957d8880a33da0d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 20:02:18 +1100 Subject: [PATCH 053/398] version bump Program plugin --- Plugins/Flow.Launcher.Plugin.Program/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/plugin.json b/Plugins/Flow.Launcher.Plugin.Program/plugin.json index 93e5079c2..d110124ff 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Program/plugin.json @@ -4,7 +4,7 @@ "Name": "Program", "Description": "Search programs in Flow.Launcher", "Author": "qianlifeng", - "Version": "1.4.1", + "Version": "1.4.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Program.dll", From 575ff31ce56e056ac4f97f0cb5faa1c46d20f612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 17:17:03 +0800 Subject: [PATCH 054/398] change format Initialize out variable inline, and use discard varable for unused variable --- .../Programs/UWP.cs | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 69afecb4f..855a11c86 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -56,10 +56,9 @@ namespace Flow.Launcher.Plugin.Program.Programs var namespaces = XmlNamespaces(path); InitPackageVersion(namespaces); - IStream stream; const uint noAttribute = 0x80; const Stgm exclusiveRead = Stgm.Read | Stgm.ShareExclusive; - var hResult = SHCreateStreamOnFileEx(path, exclusiveRead, noAttribute, false, null, out stream); + var hResult = SHCreateStreamOnFileEx(path, exclusiveRead, noAttribute, false, null, out IStream stream); if (hResult == Hresult.Ok) { @@ -345,14 +344,13 @@ namespace Flow.Launcher.Plugin.Program.Programs private async void Launch(IPublicAPI api) { var appManager = new ApplicationActivationHelper.ApplicationActivationManager(); - uint unusedPid; const string noArgs = ""; const ApplicationActivationHelper.ActivateOptions noFlags = ApplicationActivationHelper.ActivateOptions.None; await Task.Run(() => { try { - appManager.ActivateApplication(UserModelId, noArgs, noFlags, out unusedPid); + appManager.ActivateApplication(UserModelId, noArgs, noFlags, out _); } catch (Exception) { @@ -366,19 +364,13 @@ namespace Flow.Launcher.Plugin.Program.Programs public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP package) { // This is done because we cannot use the keyword 'out' along with a property - string tmpUserModelId; - string tmpUniqueIdentifier; - string tmpDisplayName; - string tmpDescription; - string tmpBackgroundColor; - string tmpEntryPoint; - manifestApp.GetAppUserModelId(out tmpUserModelId); - manifestApp.GetAppUserModelId(out tmpUniqueIdentifier); - manifestApp.GetStringValue("DisplayName", out tmpDisplayName); - manifestApp.GetStringValue("Description", out tmpDescription); - manifestApp.GetStringValue("BackgroundColor", out tmpBackgroundColor); - manifestApp.GetStringValue("EntryPoint", out tmpEntryPoint); + manifestApp.GetAppUserModelId(out string tmpUserModelId); + manifestApp.GetAppUserModelId(out string tmpUniqueIdentifier); + manifestApp.GetStringValue("DisplayName", out string tmpDisplayName); + manifestApp.GetStringValue("Description", out string tmpDescription); + manifestApp.GetStringValue("BackgroundColor", out string tmpBackgroundColor); + manifestApp.GetStringValue("EntryPoint", out string tmpEntryPoint); UserModelId = tmpUserModelId; UniqueIdentifier = tmpUniqueIdentifier; @@ -464,9 +456,8 @@ namespace Flow.Launcher.Plugin.Program.Programs }; if (logoKeyFromVersion.ContainsKey(Package.Version)) { - string logoUri; var key = logoKeyFromVersion[Package.Version]; - app.GetStringValue(key, out logoUri); + app.GetStringValue(key, out string logoUri); return logoUri; } else From 8a2a71000ec369bf17d57d3c9311840e731ea41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 17:58:15 +0800 Subject: [PATCH 055/398] Use Channel instead of BufferBlock --- Flow.Launcher/ViewModel/MainViewModel.cs | 31 ++++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index a12912b67..0cf4b11dc 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -19,6 +19,7 @@ using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using Flow.Launcher.Infrastructure.Logger; +using System.Threading.Channels; namespace Flow.Launcher.ViewModel { @@ -46,7 +47,7 @@ namespace Flow.Launcher.ViewModel private readonly Internationalization _translator = InternationalizationManager.Instance; - private BufferBlock _resultsUpdateQueue; + private Channel _resultsUpdateQueue; private Task _resultsViewUpdateTask; #endregion @@ -85,18 +86,21 @@ namespace Flow.Launcher.ViewModel private void RegisterViewUpdate() { - _resultsUpdateQueue = new BufferBlock(); + _resultsUpdateQueue = Channel.CreateUnbounded(); _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); async Task updateAction() { var queue = new Dictionary(); - while (await _resultsUpdateQueue.OutputAvailableAsync()) + var queueReader = _resultsUpdateQueue.Reader; + + // it is not supposed to be false because it won't be complete + while (await queueReader.WaitToReadAsync()) { queue.Clear(); await Task.Delay(20); - while (_resultsUpdateQueue.TryReceive(out var item)) + await foreach (var item in queueReader.ReadAllAsync()) { if (!item.Token.IsCancellationRequested) queue[item.ID] = item; @@ -104,6 +108,8 @@ namespace Flow.Launcher.ViewModel UpdateResultView(queue.Values); } + + Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); }; void continueAction(Task t) @@ -128,7 +134,10 @@ namespace Flow.Launcher.ViewModel if (e.Query.RawQuery == QueryText) // TODO: allow cancellation { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - _resultsUpdateQueue.Post(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken)); + if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken))) + { + Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); + }; } }; } @@ -529,9 +538,15 @@ namespace Flow.Launcher.ViewModel await Task.Yield(); var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); - if (!currentCancellationToken.IsCancellationRequested && results != null) - _resultsUpdateQueue.Post(new ResultsForUpdate(results, plugin.Metadata, query, - currentCancellationToken)); + if (currentCancellationToken.IsCancellationRequested || results == null) + { + return; + } + + if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) + { + Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); + }; } }, currentCancellationToken) .ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), From d36140c51ce0b3c1a729092c1efff8f218e59582 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 15 Feb 2021 21:14:50 +1100 Subject: [PATCH 056/398] version bump PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index 4cba7ec83..f95c0d60d 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.6.4", + "Version": "1.7.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From a7811f312c1add5f6a16e44970ebd010173abed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 18:19:30 +0800 Subject: [PATCH 057/398] fix an issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 0cf4b11dc..3e71adcd1 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -98,15 +98,15 @@ namespace Flow.Launcher.ViewModel // it is not supposed to be false because it won't be complete while (await queueReader.WaitToReadAsync()) { - queue.Clear(); await Task.Delay(20); - await foreach (var item in queueReader.ReadAllAsync()) + while (queueReader.TryRead(out var item)) { if (!item.Token.IsCancellationRequested) queue[item.ID] = item; } UpdateResultView(queue.Values); + queue.Clear(); } Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); From 9ebebfd0ef9fcaaada22c096adb55f093969e08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 18:27:02 +0800 Subject: [PATCH 058/398] No need to cancel error reporting --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index e342a5f94..9caac1a47 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -213,7 +213,6 @@ namespace Flow.Launcher.Core.Plugin { using var standardError = process.StandardError; var error = await standardError.ReadToEndAsync(); - token.ThrowIfCancellationRequested(); if (!string.IsNullOrEmpty(error)) { From b017bdeed435f810909e2afd2c9f49bb41ba3f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 15 Feb 2021 18:37:42 +0800 Subject: [PATCH 059/398] fix format --- Flow.Launcher/ViewModel/MainViewModel.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 3e71adcd1..90bdc38c8 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -538,10 +538,7 @@ namespace Flow.Launcher.ViewModel await Task.Yield(); var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); - if (currentCancellationToken.IsCancellationRequested || results == null) - { - return; - } + if (currentCancellationToken.IsCancellationRequested || results == null) return; if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) { From 033ce3051b4abd06318d9b8234775c29ff325695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 01:41:02 +0800 Subject: [PATCH 060/398] filter desctiption existance before passing it to distinct --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 372d36524..8657f0ec0 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -475,9 +475,10 @@ namespace Flow.Launcher.Plugin.Program.Programs return programs.GroupBy(p => p.FullPath.ToLower()) .SelectMany(g => { - if (g.Count() > 1) - return DistinctBy(g.Where(p => !string.IsNullOrEmpty(p.Description)), x => x.Description); - return g; + var temp = g.Where(g => !string.IsNullOrEmpty(g.Description)).ToList(); + if (temp.Any()) + return DistinctBy(temp, x => x.Description); + return g.Take(1); }).ToArray(); } From 350499a4801a9a60ba00a811b79bc7785b4da33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 02:12:28 +0800 Subject: [PATCH 061/398] Optimize Logic and code --- Flow.Launcher/ViewModel/MainViewModel.cs | 17 +++++++++-------- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 9 +-------- Flow.Launcher/ViewModel/ResultsViewModel.cs | 11 ++++------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 90bdc38c8..c1382e51e 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -47,7 +47,7 @@ namespace Flow.Launcher.ViewModel private readonly Internationalization _translator = InternationalizationManager.Instance; - private Channel _resultsUpdateQueue; + private ChannelWriter _resultsUpdateChannelWriter; private Task _resultsViewUpdateTask; #endregion @@ -86,20 +86,21 @@ namespace Flow.Launcher.ViewModel private void RegisterViewUpdate() { - _resultsUpdateQueue = Channel.CreateUnbounded(); + var resultUpdateChannel = Channel.CreateUnbounded(); + _resultsUpdateChannelWriter = resultUpdateChannel.Writer; _resultsViewUpdateTask = Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); async Task updateAction() { var queue = new Dictionary(); - var queueReader = _resultsUpdateQueue.Reader; + var channelReader = resultUpdateChannel.Reader; // it is not supposed to be false because it won't be complete - while (await queueReader.WaitToReadAsync()) + while (await channelReader.WaitToReadAsync()) { await Task.Delay(20); - while (queueReader.TryRead(out var item)) + while (channelReader.TryRead(out var item)) { if (!item.Token.IsCancellationRequested) queue[item.ID] = item; @@ -134,7 +135,7 @@ namespace Flow.Launcher.ViewModel if (e.Query.RawQuery == QueryText) // TODO: allow cancellation { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - if (!_resultsUpdateQueue.Writer.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); }; @@ -539,8 +540,8 @@ namespace Flow.Launcher.ViewModel var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); if (currentCancellationToken.IsCancellationRequested || results == null) return; - - if (!_resultsUpdateQueue.Writer.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"); }; diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index be48f53c1..87d526fd6 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -6,7 +6,7 @@ using System.Threading; namespace Flow.Launcher.ViewModel { - public class ResultsForUpdate + public struct ResultsForUpdate { public List Results { get; } @@ -16,13 +16,6 @@ namespace Flow.Launcher.ViewModel public Query Query { get; } public CancellationToken Token { get; } - public ResultsForUpdate(List results, string resultID, CancellationToken token) - { - Results = results; - ID = resultID; - Token = token; - } - public ResultsForUpdate(List results, PluginMetadata metadata, Query query, CancellationToken token) { Results = results; diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index feab3a751..41f16f4f2 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -183,13 +183,12 @@ namespace Flow.Launcher.ViewModel private List NewResults(List newRawResults, string resultId) { if (newRawResults.Count == 0) - return Results.ToList(); + return Results; - var results = Results as IEnumerable; var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)); - return results.Where(r => r.Result.PluginID != resultId) + return Results.Where(r => r.Result.PluginID != resultId) .Concat(newResults) .OrderByDescending(r => r.Result.Score) .ToList(); @@ -198,11 +197,9 @@ namespace Flow.Launcher.ViewModel private List NewResults(IEnumerable resultsForUpdates) { if (!resultsForUpdates.Any()) - return Results.ToList(); + return Results; - var results = Results as IEnumerable; - - return results.Where(r => r != null && !resultsForUpdates.Any(u => u.Metadata.ID == r.Result.PluginID)) + return Results.Where(r => r != null && !resultsForUpdates.Any(u => u.ID == r.Result.PluginID)) .Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings))) .OrderByDescending(rv => rv.Result.Score) .ToList(); From abd721a06cff43e1490843b8f9b995e064cc1c62 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 16 Feb 2021 06:14:13 +1100 Subject: [PATCH 062/398] fix formatting --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 9caac1a47..7a088bd09 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -31,7 +31,6 @@ namespace Flow.Launcher.Core.Plugin protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); protected abstract string ExecuteContextMenu(Result selectedResult); - public List LoadContextMenus(Result selectedResult) { string output = ExecuteContextMenu(selectedResult); @@ -235,8 +234,6 @@ namespace Flow.Launcher.Core.Plugin } } - - public async Task> QueryAsync(Query query, CancellationToken token) { var output = await ExecuteQueryAsync(query, token); @@ -257,4 +254,4 @@ namespace Flow.Launcher.Core.Plugin return Task.CompletedTask; } } -} \ No newline at end of file +} From 307cb305f064f2dd21886f7f163a1a789e3cad3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 10:50:48 +0800 Subject: [PATCH 063/398] Remove unused check for Stream length --- Flow.Launcher.Core/Plugin/PluginManager.cs | 4 ++-- Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs | 4 ++-- .../SuggestionSources/Baidu.cs | 2 +- .../SuggestionSources/Bing.cs | 7 ++----- .../SuggestionSources/Google.cs | 7 ++----- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 700a7d509..66eb0f0ec 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -195,11 +195,11 @@ namespace Flow.Launcher.Core.Plugin catch (OperationCanceledException) { // null will be fine since the results will only be added into queue if the token hasn't been cancelled - return results = null; + return null; } catch (Exception e) { - Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); + Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); } return results; diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs index f76e28112..4135bb59a 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Main.cs @@ -123,12 +123,12 @@ namespace Flow.Launcher.Plugin.WebSearch var source = _settings.SelectedSuggestion; if (source != null) { - var suggestions = await source.Suggestions(keyword, token); + var suggestions = await source.Suggestions(keyword, token).ConfigureAwait(false); if (token.IsCancellationRequested) return null; - var resultsFromSuggestion = suggestions.Select(o => new Result + var resultsFromSuggestion = suggestions?.Select(o => new Result { Title = o, SubTitle = subtitle, diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index b7e2017f9..385a9f8b5 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -32,7 +32,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources catch (HttpRequestException e) { Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); - return new List(); + return null; } if (string.IsNullOrEmpty(result)) return new List(); diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index ffde2fda2..e505d78cf 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -24,16 +24,13 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources const string api = "https://api.bing.com/qsonhs.aspx?q="; using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false); - - if (resultStream.Length == 0) - return new List(); // this handles the cancellation - + json = (await JsonDocument.ParseAsync(resultStream, cancellationToken: token)).RootElement.GetProperty("AS"); } catch (TaskCanceledException) { - return null; + return new List(); } catch (HttpRequestException e) { diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index c33ebd7e1..928c7aaa2 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -24,19 +24,16 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); - if (resultStream.Length == 0) - return new List(); - json = await JsonDocument.ParseAsync(resultStream); } catch (TaskCanceledException) { - return null; + return new List(); } catch (HttpRequestException e) { - Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); + // Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); return new List(); } catch (JsonException e) From 493a50382d0bf4b5a02ab10433544412921c9144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 11:01:23 +0800 Subject: [PATCH 064/398] Update Readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9af47a5ee..3780978e3 100644 --- a/README.md +++ b/README.md @@ -76,11 +76,11 @@ Yes please, submit an issue to let us know. ## Developing/Debugging -Flow Launcher's target framework is .Net Core 3.1 +Flow Launcher's target framework is .Net Core 5 Install Visual Studio 2019 -Install .Net Core 3.1 SDK via Visual Studio installer or manually from [here](https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.201-windows-x64-installer) +Install .Net Core 5 SDK via Visual Studio installer or manually from [here](https://dotnet.microsoft.com/download/dotnet/5.0) ## Documentation From 2bc290a30208d81588b19a38c36fd542d956dae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 11:27:12 +0800 Subject: [PATCH 065/398] uncomment exception --- .../Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index 928c7aaa2..c075f4d5d 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -33,7 +33,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources } catch (HttpRequestException e) { - // Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); + Log.Exception("|Google.Suggestions|Can't get suggestion from google", e); return new List(); } catch (JsonException e) From f14a5b92296f2d7cba3cf55cfb69c1e217725d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 15:54:11 +0800 Subject: [PATCH 066/398] add default win32, so we should not contain null --- .../Programs/Win32.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 8657f0ec0..aeb35ea39 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -35,6 +35,20 @@ namespace Flow.Launcher.Plugin.Program.Programs private const string ShortcutExtension = "lnk"; private const string ExeExtension = "exe"; + private static readonly Win32 Default = new Win32() + { + Name = string.Empty, + Description = string.Empty, + IcoPath = string.Empty, + FullPath = string.Empty, + LnkResolvedPath = null, + ParentDirectory = string.Empty, + ExecutableName = null, + UniqueIdentifier = string.Empty, + Valid = false, + Enabled = false + }; + public Result Result(string query, IPublicAPI api) { @@ -423,12 +437,12 @@ namespace Flow.Launcher.Plugin.Program.Programs private static Win32 GetProgramFromPath(string path) { if (string.IsNullOrEmpty(path)) - return null; + return Default; path = Environment.ExpandEnvironmentVariables(path); if (!File.Exists(path)) - return null; + return Default; var entry = Win32Program(path); @@ -504,7 +518,6 @@ namespace Flow.Launcher.Plugin.Program.Programs programs = programs.Concat(startMenu); } - return ProgramsHasher(programs.Where(p => p != null)); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. From 5fe833f7f2dff74edd65b900c400979f7b2e7856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 15:55:44 +0800 Subject: [PATCH 067/398] Take user added program outside the hasher --- .../Flow.Launcher.Plugin.Program/Programs/Win32.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index aeb35ea39..a186b6613 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -484,7 +484,7 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - private static Win32[] ProgramsHasher(IEnumerable programs) + private static IEnumerable ProgramsHasher(IEnumerable programs) { return programs.GroupBy(p => p.FullPath.ToLower()) .SelectMany(g => @@ -503,8 +503,6 @@ namespace Flow.Launcher.Plugin.Program.Programs { var programs = Enumerable.Empty(); - var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes); - programs = programs.Concat(unregistered); if (settings.EnableRegistrySource) { @@ -518,7 +516,13 @@ namespace Flow.Launcher.Plugin.Program.Programs programs = programs.Concat(startMenu); } - return ProgramsHasher(programs.Where(p => p != null)); + programs = ProgramsHasher(programs.Where(p => p != null)); + + var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes); + + programs = programs.Concat(unregistered); + + return programs.ToArray(); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. catch (Exception e) From df2be9b82836c46545bb8639b9ab97178a9d4038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 16:09:55 +0800 Subject: [PATCH 068/398] seperate user program sources and autoindex program source and distinct prior to user one --- .../Programs/Win32.cs | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index a186b6613..773d0c6b6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -14,11 +14,12 @@ using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.Infrastructure.Logger; using System.Diagnostics; using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; +using System.Diagnostics.CodeAnalysis; namespace Flow.Launcher.Plugin.Program.Programs { [Serializable] - public class Win32 : IProgram + public class Win32 : IProgram, IEquatable { public string Name { get; set; } public string UniqueIdentifier { get; set; } @@ -503,26 +504,27 @@ namespace Flow.Launcher.Plugin.Program.Programs { var programs = Enumerable.Empty(); + var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes); + + programs = programs.Concat(unregistered); + + var autoIndexPrograms = Enumerable.Empty(); if (settings.EnableRegistrySource) { var appPaths = AppPathsPrograms(settings.ProgramSuffixes); - programs = programs.Concat(appPaths); + autoIndexPrograms = autoIndexPrograms.Concat(appPaths); } if (settings.EnableStartMenuSource) { var startMenu = StartMenuPrograms(settings.ProgramSuffixes); - programs = programs.Concat(startMenu); + autoIndexPrograms = autoIndexPrograms.Concat(startMenu); } - programs = ProgramsHasher(programs.Where(p => p != null)); + autoIndexPrograms = ProgramsHasher(autoIndexPrograms); - var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes); - - programs = programs.Concat(unregistered); - - return programs.ToArray(); + return programs.Concat(autoIndexPrograms).Distinct().ToArray(); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. catch (Exception e) @@ -540,5 +542,18 @@ namespace Flow.Launcher.Plugin.Program.Programs } #endif } + + public override int GetHashCode() + { + return UniqueIdentifier.GetHashCode(); + } + + public bool Equals([AllowNull] Win32 other) + { + if (other == null) + return false; + + return UniqueIdentifier == other.UniqueIdentifier; + } } } \ No newline at end of file From 4890f796264e262284291aee9d2b3413ec44583a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Tue, 16 Feb 2021 16:15:54 +0800 Subject: [PATCH 069/398] change download link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fcf7a46f4..7c24261e9 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Flow Launcher's target framework is .Net Core 5 Install Visual Studio 2019 -Install .Net Core 5 SDK via Visual Studio installer or manually from [here](https://dotnet.microsoft.com/download/dotnet/5.0) +Install .Net Core 5 SDK via Visual Studio installer or manually from [here](https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.103-windows-x64-installer) ## Documentation From dfd71482294f2a7de34a16ab5e5237c9df2cf378 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Wed, 17 Feb 2021 08:50:33 +1100 Subject: [PATCH 070/398] remove duplicate entry with quick access results --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 2af09bf2c..899412be6 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -38,7 +38,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search var quickaccessLinks = QuickAccess.AccessLinkListMatched(query, settings.QuickAccessLinks); if (quickaccessLinks.Count > 0) - results.AddRange(quickaccessLinks); + results.Union(quickaccessLinks); var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 9aa54fb83..63ca66a1e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.7.0", + "Version": "1.7.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", From b0ce545116ffd0b7906d7601337eb888e2959277 Mon Sep 17 00:00:00 2001 From: Zero Date: Wed, 17 Feb 2021 19:33:07 +0800 Subject: [PATCH 071/398] Update README.md simplify rep README --- README.md | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index f0c08d8a0..9c1ae2583 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be Remember to star it, flow will love you more :) ## Features + ![The Flow](https://user-images.githubusercontent.com/26427004/82151677-fa9c7100-989f-11ea-9143-81de60aaf07d.gif) - Search everything from applications, files, bookmarks, YouTube, Twitter and more. All from the comfort of your keyboard without ever touching the mouse. @@ -26,31 +27,6 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be - Support of wide range of plugins. - Fully portable. -## Running Flow Launcher - -| [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) | -| ---------------------------------------------------------------------------------- | - -Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up. - -**Integrations** - - If you use Python plugins: - - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. - - Add Python to `%PATH%` or set it in flow's settings. - - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. - - Start to launch your Python plugins. - - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). - -**Usage** -- Open flow's search window: Alt+Space is the default hotkey. -- Open context menu: Ctrl+O/Shift+Enter. -- Cancel/Return to previous screen: Esc. -- Install/Uninstall plugins: in the search window, type `wpm install/uninstall` + the plugin name. -- Saved user settings are located: - - If using roaming: `%APPDATA%\FlowLauncher` - - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` -- Logs are saved along with your user settings folder - ## Status Flow is under heavy development, but the code base is stable, so contributions are very welcome. If you would like to help maintain it, please do not hesistate to get in touch. @@ -63,7 +39,7 @@ You will find the main goals of flow placed under Projects board, so feel free t Get in touch if you like to join the Flow-Launcher Team and help build this great tool. -**Question/Suggestion** +## Question/Suggestion Yes please, submit an issue to let us know. @@ -79,4 +55,4 @@ Install .Net Core 3.1 SDK via Visual Studio installer or manually from [here](ht ## Documentation -[Wiki](https://github.com/Flow-Launcher/Flow.Launcher/wiki) +https://flow-launcher.github.io/docs From 4a3ad0854868f87e30401cf4bda986457db5bf3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Wed, 17 Feb 2021 22:36:35 +0800 Subject: [PATCH 072/398] Use IReadOnlyCollection to avoid avoid duplicate iteration Add distinct to NewResults to try avoiding duplicate result --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- Flow.Launcher/ViewModel/ResultsViewModel.cs | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index c1382e51e..e0fc9ee67 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -778,7 +778,7 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void UpdateResultView(IEnumerable resultsForUpdates) + public void UpdateResultView(IReadOnlyCollection resultsForUpdates) { if (!resultsForUpdates.Any()) return; diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 41f16f4f2..2bdafca62 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -1,16 +1,13 @@ -using System; +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.Plugin; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using System.Threading; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; -using Flow.Launcher.Infrastructure.UserSettings; -using Flow.Launcher.Plugin; namespace Flow.Launcher.ViewModel { @@ -146,7 +143,7 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void AddResults(IEnumerable resultsForUpdates, CancellationToken token) + public void AddResults(IReadOnlyCollection resultsForUpdates, CancellationToken token) { var newResults = NewResults(resultsForUpdates); @@ -191,10 +188,11 @@ namespace Flow.Launcher.ViewModel return Results.Where(r => r.Result.PluginID != resultId) .Concat(newResults) .OrderByDescending(r => r.Result.Score) + .Distinct() .ToList(); } - private List NewResults(IEnumerable resultsForUpdates) + private List NewResults(IReadOnlyCollection resultsForUpdates) { if (!resultsForUpdates.Any()) return Results; @@ -202,6 +200,7 @@ namespace Flow.Launcher.ViewModel return Results.Where(r => r != null && !resultsForUpdates.Any(u => u.ID == r.Result.PluginID)) .Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings))) .OrderByDescending(rv => rv.Result.Score) + .Distinct() .ToList(); } #endregion From 8b30d37ff767b93f43824c834f0eb52af03f917e Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 18 Feb 2021 08:09:51 +1100 Subject: [PATCH 073/398] update --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 899412be6..2af09bf2c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -38,7 +38,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search var quickaccessLinks = QuickAccess.AccessLinkListMatched(query, settings.QuickAccessLinks); if (quickaccessLinks.Count > 0) - results.Union(quickaccessLinks); + results.AddRange(quickaccessLinks); var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch); diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 63ca66a1e..9aa54fb83 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.7.1", + "Version": "1.7.0", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", From 764aa4e05cf4de4c8a2207a83e1e4c7b9a7c9da5 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 18 Feb 2021 08:57:09 +1100 Subject: [PATCH 074/398] remove Docs folder --- Docs/port-plugins.md | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 Docs/port-plugins.md diff --git a/Docs/port-plugins.md b/Docs/port-plugins.md deleted file mode 100644 index d12830124..000000000 --- a/Docs/port-plugins.md +++ /dev/null @@ -1,17 +0,0 @@ -# Porting Wox Plugins - -Note: -- When porting, please keep the author's commit history -- Flow Launcher targets .Net Core 3.1, so plugins should also be upgraded to keep the continuity of future developments - -Steps: -1. to start off, you can fork/create a new repo, either way the project's commit history must be kept. If it's forked, you can just start updating it. If it's a new repo, do this by first cloning the repo, then add your new repo as a new repo remote, remove the original remote and then push to it. -2. use try convert tool from https://github.com/dotnet/try-convert -3. try-convert -w path-to-folder-or-solution-or-project -4. May need to fix on the project file, a good template to follow is the [Explorer plugin](https://github.com/Flow-Launcher/Flow.Launcher/blob/dev/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj) project: - - fix to netcoreapp3.1 - - set the output location as 'Output\Release\' - - add true and false - - bump version to 2.0.0 and fix up any missing attributes if neccessary -5. update code and fix plugin's setting layout if neccessary -6. update readme to indicate where this port is from and the original author of the project \ No newline at end of file From cba47855c7970543380a916226b6e3edba909f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 18 Feb 2021 06:38:43 +0800 Subject: [PATCH 075/398] Use Hashset and customized equality comparator to filter result --- .../Search/SearchManager.cs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 899412be6..66d11cc39 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -22,9 +22,25 @@ namespace Flow.Launcher.Plugin.Explorer.Search this.settings = settings; } + private class PathEqualityComparator : IEqualityComparer + { + private static PathEqualityComparator instance; + public static PathEqualityComparator Instance => instance ??= new PathEqualityComparator(); + public bool Equals(Result x, Result y) + { + return x.SubTitle == y.SubTitle; + } + + public int GetHashCode(Result obj) + { + return obj.SubTitle.GetHashCode(); + } + + } + internal async Task> SearchAsync(Query query, CancellationToken token) { - var results = new List(); + var results = new HashSet(PathEqualityComparator.Instance); var querySearch = query.Search; @@ -50,9 +66,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search if (!querySearch.IsLocationPathString() && !isEnvironmentVariablePath) { - results.AddRange(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); + results.UnionWith(await WindowsIndexFilesAndFoldersSearchAsync(query, querySearch, token).ConfigureAwait(false)); - return results; + return results.ToList(); } var locationPath = querySearch; @@ -62,7 +78,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search // Check that actual location exists, otherwise directory search will throw directory not found exception if (!FilesFolders.LocationExists(FilesFolders.ReturnPreviousDirectoryIfIncompleteString(locationPath))) - return results; + return results.ToList(); var useIndexSearch = UseWindowsIndexForDirectorySearch(locationPath); @@ -79,9 +95,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search token.ThrowIfCancellationRequested(); - results.AddRange(directoryResult); + results.UnionWith(directoryResult); - return results; + return results.ToList(); } private async Task> WindowsIndexFileContentSearchAsync(Query query, string querySearchString, CancellationToken token) From 73ee3437f32f422b363cb5b8afd15a0f32f67366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Thu, 18 Feb 2021 06:41:56 +0800 Subject: [PATCH 076/398] Use UnionWith --- Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index 7869b083c..ff4477336 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -54,7 +54,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search var quickaccessLinks = QuickAccess.AccessLinkListMatched(query, settings.QuickAccessLinks); if (quickaccessLinks.Count > 0) - results.AddRange(quickaccessLinks); + results.UnionWith(quickaccessLinks); var isEnvironmentVariable = EnvironmentVariables.IsEnvironmentVariableSearch(querySearch); From ed82662da2b0d625e83199945890ed2394e1a915 Mon Sep 17 00:00:00 2001 From: Zero Date: Thu, 18 Feb 2021 22:00:06 +0800 Subject: [PATCH 077/398] Update README.md --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 5fad0367b..8c652593c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,33 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be - Save file or folder locations for quick access. - Fully portable. +## Running Flow Launcher + +| [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) | +| ---------------------------------------------------------------------------------- | + +Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up. + +### Integrations + +- If you use Python plugins: + - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. + - Add Python to `%PATH%` or set it in flow's settings. + - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. + - Start to launch your Python plugins. +- Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). + +### Usage + +- Open flow's search window: Alt+Space is the default hotkey. +- Open context menu: Ctrl+O/Shift+Enter. +- Cancel/Return to previous screen: Esc. +- Install/Uninstall plugins: in the search window, type `wpm install/uninstall` + the plugin name. +- Saved user settings are located: + - If using roaming: `%APPDATA%\FlowLauncher` + - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` +- Logs are saved along with your user settings folder + ## Status Flow is under heavy development, but the code base is stable, so contributions are very welcome. If you would like to help maintain it, please do not hesistate to get in touch. From 43f48de2a3444159aae2da75d6a0c3437da9baef Mon Sep 17 00:00:00 2001 From: Zero Date: Thu, 18 Feb 2021 22:04:25 +0800 Subject: [PATCH 078/398] Update README.md missing SOFTPEDIA EDITOR'S PICK --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8c652593c..9d52d268a 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be - Save file or folder locations for quick access. - Fully portable. +[ **SOFTPEDIA EDITOR'S PICK**](https://www.softpedia.com/get/System/Launchers-Shutdown-Tools/Flow-Launcher.shtml) + ## Running Flow Launcher | [Windows 7 and up](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) | From 81f069d8d20f5a0bc50c4f5b1591fbd6880fb3cb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 07:33:50 +1100 Subject: [PATCH 079/398] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d52d268a..64fe90d50 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Windows may complain about security due to code not being signed, this will be c - Open flow's search window: Alt+Space is the default hotkey. - Open context menu: Ctrl+O/Shift+Enter. - Cancel/Return to previous screen: Esc. -- Install/Uninstall plugins: in the search window, type `wpm install/uninstall` + the plugin name. +- Install/Uninstall plugins: in the search window, type `pm install/uninstall` + the plugin name. - Saved user settings are located: - If using roaming: `%APPDATA%\FlowLauncher` - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` From 926f8362f2bb8200f2c15c5884128ca4b6fa4ed9 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 08:01:54 +1100 Subject: [PATCH 080/398] Update README.md --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 64fe90d50..9e89053b5 100644 --- a/README.md +++ b/README.md @@ -39,25 +39,25 @@ Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at be Windows may complain about security due to code not being signed, this will be completed at a later stage. If you downloaded from this repo, you are good to continue the set up. ### Integrations - -- If you use Python plugins: - - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. - - Add Python to `%PATH%` or set it in flow's settings. - - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. - - Start to launch your Python plugins. -- Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). + - If you use Python plugins: + - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. + - Add Python to `%PATH%` or set it in flow's settings. + - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. + - Start to launch your Python plugins. + - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). ### Usage - - Open flow's search window: Alt+Space is the default hotkey. -- Open context menu: Ctrl+O/Shift+Enter. +- Open context menu: on the selected result, press Ctrl+O/Shift+Enter. - Cancel/Return to previous screen: Esc. -- Install/Uninstall plugins: in the search window, type `pm install/uninstall` + the plugin name. +- Install/Uninstall/Update plugins: in the search window, type `pm` `install`/`uninstall`/`update` + the plugin name. - Saved user settings are located: - If using roaming: `%APPDATA%\FlowLauncher` - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` - Logs are saved along with your user settings folder +[More tips](https://flow-launcher.github.io/docs/#/usage-tips) + ## Status Flow is under heavy development, but the code base is stable, so contributions are very welcome. If you would like to help maintain it, please do not hesistate to get in touch. From a3c61c26cc632e41444252295654e59c3c391aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Fri, 19 Feb 2021 05:05:03 +0800 Subject: [PATCH 081/398] don't remove setting when updating --- .../PluginsManager.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index c2e392b51..e671e2e91 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -233,7 +233,7 @@ namespace Flow.Launcher.Plugin.PluginsManager Context.API.GetTranslation("plugin_pluginsmanager_update_title"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { - Uninstall(x.PluginExistingMetadata); + Uninstall(x.PluginExistingMetadata, false); var downloadToFilePath = Path.Combine(DataLocation.PluginsDirectory, $"{x.Name}-{x.NewVersion}.zip"); @@ -414,10 +414,13 @@ namespace Flow.Launcher.Plugin.PluginsManager return Search(results, uninstallSearch); } - private void Uninstall(PluginMetadata plugin) + private void Uninstall(PluginMetadata plugin, bool removedSetting = true) { - PluginManager.Settings.Plugins.Remove(plugin.ID); - PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); + if (removedSetting) + { + PluginManager.Settings.Plugins.Remove(plugin.ID); + PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID); + } // Marked for deletion. Will be deleted on next start up using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")); From 4fdcdcdcc08c2c4bea0434beea58fe77a9e1d153 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 08:12:46 +1100 Subject: [PATCH 082/398] version bump PluginsManager --- Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json index f95c0d60d..7d32a40c5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json @@ -6,7 +6,7 @@ "Name": "Plugins Manager", "Description": "Management of installing, uninstalling or updating Flow Launcher plugins", "Author": "Jeremy Wu", - "Version": "1.7.0", + "Version": "1.7.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll", From 95da58534318f7f85ad3c8f82301c77bb0f3de4d Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 08:44:36 +1100 Subject: [PATCH 083/398] add documentation badge --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e89053b5..a0ed2d5e8 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,12 @@

-![Maintenance](https://img.shields.io/maintenance/yes/2021) +![Maintenance](https://img.shields.io/maintenance/yes/3000) [![Build status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true&retina=true)](https://ci.appveyor.com/project/JohnTheGr8/flow-launcher/branch/dev) [![Github All Releases](https://img.shields.io/github/downloads/Flow-Launcher/Flow.Launcher/total.svg)](https://github.com/Flow-Launcher/Flow.Launcher/releases) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) ![GitHub Release Date](https://img.shields.io/github/release-date/Flow-Launcher/Flow.Launcher) +[![Documentation](https://img.shields.io/badge/Documentation-blue)](https://flow-launcher.github.io/docs) [![Discord](https://img.shields.io/discord/727828229250875472?color=7389D8&labelColor=6A7EC2&label=Community&logo=discord&logoColor=white)](https://discord.gg/AvgAQgh) Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at being more than an app launcher, it searches, integrates and expands on functionalities. Flow will continue to evolve, designed to be open and built with the community at heart. From 8b81ac004aa623e1f4a71120e02f4772573eae81 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 08:46:56 +1100 Subject: [PATCH 084/398] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0ed2d5e8..6bc6182dc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Github All Releases](https://img.shields.io/github/downloads/Flow-Launcher/Flow.Launcher/total.svg)](https://github.com/Flow-Launcher/Flow.Launcher/releases) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) ![GitHub Release Date](https://img.shields.io/github/release-date/Flow-Launcher/Flow.Launcher) -[![Documentation](https://img.shields.io/badge/Documentation-blue)](https://flow-launcher.github.io/docs) +[![Documentation](https://img.shields.io/badge/Documentation-7389D8)](https://flow-launcher.github.io/docs) [![Discord](https://img.shields.io/discord/727828229250875472?color=7389D8&labelColor=6A7EC2&label=Community&logo=discord&logoColor=white)](https://discord.gg/AvgAQgh) Flow Launcher. Dedicated to make your workflow flow more seamlessly. Aimed at being more than an app launcher, it searches, integrates and expands on functionalities. Flow will continue to evolve, designed to be open and built with the community at heart. From b2ae8d08c273e0fc25bb57e7232c7d6052ce8d86 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 08:55:11 +1100 Subject: [PATCH 085/398] add contributors badge --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bc6182dc..6d7984520 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,9 @@ ![Maintenance](https://img.shields.io/maintenance/yes/3000) [![Build status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true&retina=true)](https://ci.appveyor.com/project/JohnTheGr8/flow-launcher/branch/dev) [![Github All Releases](https://img.shields.io/github/downloads/Flow-Launcher/Flow.Launcher/total.svg)](https://github.com/Flow-Launcher/Flow.Launcher/releases) -[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) ![GitHub Release Date](https://img.shields.io/github/release-date/Flow-Launcher/Flow.Launcher) +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) +![Contributors](https://badgen.net/github/contributors/Flow-Launcher/Flow.Launcher) [![Documentation](https://img.shields.io/badge/Documentation-7389D8)](https://flow-launcher.github.io/docs) [![Discord](https://img.shields.io/discord/727828229250875472?color=7389D8&labelColor=6A7EC2&label=Community&logo=discord&logoColor=white)](https://discord.gg/AvgAQgh) From 7b22622285cd8a1a28efc60cbeacc232c912d263 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 08:58:18 +1100 Subject: [PATCH 086/398] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6d7984520..ad3050885 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Windows may complain about security due to code not being signed, this will be c - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. - Add Python to `%PATH%` or set it in flow's settings. - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. + - The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`. - Start to launch your Python plugins. - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). From bb42b930e367f7547cdabcdd7350b929cc88b546 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 08:59:11 +1100 Subject: [PATCH 087/398] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad3050885..f1647fc7f 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Windows may complain about security due to code not being signed, this will be c - If you use Python plugins: - Install [Python3](https://www.python.org/downloads/), download `.exe` installer. - Add Python to `%PATH%` or set it in flow's settings. - - Use `pip` to install `flowlauncher`, cmd in `pip install flowlauncher`. + - Use `pip` to install `flowlauncher`, open cmd and type `pip install flowlauncher`. - The Python plugin may require additional modules to be installed, please ensure you check by visiting the plugin's website via `pm install` + plugin name, go to context menu and select `Open website`. - Start to launch your Python plugins. - Flow searches files and contents via Windows Index Search, to use Everything, download the plugin [here](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.Everything/releases/latest). From 493b14ff3ddfe5b16d311cca14ad38d940c3fd36 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 13:46:12 +1100 Subject: [PATCH 088/398] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index f1647fc7f..db53b8bc0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ [![Github All Releases](https://img.shields.io/github/downloads/Flow-Launcher/Flow.Launcher/total.svg)](https://github.com/Flow-Launcher/Flow.Launcher/releases) ![GitHub Release Date](https://img.shields.io/github/release-date/Flow-Launcher/Flow.Launcher) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/Flow-Launcher/Flow.Launcher)](https://github.com/Flow-Launcher/Flow.Launcher/releases/latest) -![Contributors](https://badgen.net/github/contributors/Flow-Launcher/Flow.Launcher) [![Documentation](https://img.shields.io/badge/Documentation-7389D8)](https://flow-launcher.github.io/docs) [![Discord](https://img.shields.io/discord/727828229250875472?color=7389D8&labelColor=6A7EC2&label=Community&logo=discord&logoColor=white)](https://discord.gg/AvgAQgh) @@ -57,7 +56,7 @@ Windows may complain about security due to code not being signed, this will be c - Saved user settings are located: - If using roaming: `%APPDATA%\FlowLauncher` - If using portable, by default: `%localappdata%\FlowLauncher\app-\UserData` -- Logs are saved along with your user settings folder +- Logs are saved along with your user settings folder. [More tips](https://flow-launcher.github.io/docs/#/usage-tips) From dddeeabc3a16b6dc9c00d499adaa2c385c11d27f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 13:49:56 +1100 Subject: [PATCH 089/398] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db53b8bc0..1f7dff1a5 100644 --- a/README.md +++ b/README.md @@ -88,4 +88,4 @@ Install .Net Core 3.1 SDK via Visual Studio installer or manually from [here](ht ## Documentation -https://flow-launcher.github.io/docs +Visit [here](https://flow-launcher.github.io/docs) for more information on usage, development and design documentations From abb11b22fc776a246880d80dedd475a7659389e3 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Fri, 19 Feb 2021 21:03:23 +1100 Subject: [PATCH 090/398] update --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- Flow.Launcher/ViewModel/ResultsViewModel.cs | 6 ++---- .../Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs | 1 - Plugins/Flow.Launcher.Plugin.Explorer/plugin.json | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index e0fc9ee67..c1382e51e 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -778,7 +778,7 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void UpdateResultView(IReadOnlyCollection resultsForUpdates) + public void UpdateResultView(IEnumerable resultsForUpdates) { if (!resultsForUpdates.Any()) return; diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 2bdafca62..0766c7bbc 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -143,7 +143,7 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void AddResults(IReadOnlyCollection resultsForUpdates, CancellationToken token) + public void AddResults(IEnumerable resultsForUpdates, CancellationToken token) { var newResults = NewResults(resultsForUpdates); @@ -188,11 +188,10 @@ namespace Flow.Launcher.ViewModel return Results.Where(r => r.Result.PluginID != resultId) .Concat(newResults) .OrderByDescending(r => r.Result.Score) - .Distinct() .ToList(); } - private List NewResults(IReadOnlyCollection resultsForUpdates) + private List NewResults(IEnumerable resultsForUpdates) { if (!resultsForUpdates.Any()) return Results; @@ -200,7 +199,6 @@ namespace Flow.Launcher.ViewModel return Results.Where(r => r != null && !resultsForUpdates.Any(u => u.ID == r.Result.PluginID)) .Concat(resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings))) .OrderByDescending(rv => rv.Result.Score) - .Distinct() .ToList(); } #endregion diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs index ff4477336..d5f882d5c 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/SearchManager.cs @@ -35,7 +35,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search { return obj.SubTitle.GetHashCode(); } - } internal async Task> SearchAsync(Query query, CancellationToken token) diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 9aa54fb83..63ca66a1e 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json @@ -7,7 +7,7 @@ "Name": "Explorer", "Description": "Search and manage files and folders. Explorer utilises Windows Index Search", "Author": "Jeremy Wu", - "Version": "1.7.0", + "Version": "1.7.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", From 2f9d4d129e4f278262cbe26a855186d62d21a8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Sun, 21 Feb 2021 15:46:04 +0800 Subject: [PATCH 091/398] add taskcanceledexception to updater exception catch --- Flow.Launcher.Core/Updater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 44c34968c..8f0de798c 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -87,7 +87,7 @@ namespace Flow.Launcher.Core UpdateManager.RestartApp(Constant.ApplicationFileName); } } - catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) + catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e is TaskCanceledException) { Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"), From 6a62c069c80ca8bb388a2e9ce7e363c80956b53f Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 22 Feb 2021 05:10:44 +1100 Subject: [PATCH 092/398] version bump --- SolutionAssemblyInfo.cs | 6 +++--- appveyor.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SolutionAssemblyInfo.cs b/SolutionAssemblyInfo.cs index 7f13f4130..bcddbd1bd 100644 --- a/SolutionAssemblyInfo.cs +++ b/SolutionAssemblyInfo.cs @@ -16,6 +16,6 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("1.7.1")] -[assembly: AssemblyFileVersion("1.7.1")] -[assembly: AssemblyInformationalVersion("1.7.1")] \ No newline at end of file +[assembly: AssemblyVersion("1.7.2")] +[assembly: AssemblyFileVersion("1.7.2")] +[assembly: AssemblyInformationalVersion("1.7.2")] \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 6340318d4..299a829ba 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.7.1.{build}' +version: '1.7.2.{build}' init: - ps: | From 8071a6afe41cbf31343c57a3deaed31d5fde198b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=98=E9=9F=AC=20=E5=BC=A0?= Date: Mon, 22 Feb 2021 11:46:31 +0800 Subject: [PATCH 093/398] fix potential uri format issue causing program crash --- Flow.Launcher.Infrastructure/Http/Http.cs | 40 ++++++++++++++--------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 3a3e770a5..e8cf39880 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -50,25 +50,35 @@ namespace Flow.Launcher.Infrastructure.Http ///