diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000000..4596aa08441 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,15 @@ + + + + + false + + + false + + + + \ No newline at end of file diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 87c390d3414..9f146a457fb 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -54,12 +54,8 @@ - - - - diff --git a/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs new file mode 100644 index 00000000000..b9b878a7bda --- /dev/null +++ b/Flow.Launcher.Core/Plugin/PluginAssemblyLoader.cs @@ -0,0 +1,57 @@ +using Flow.Launcher.Infrastructure; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace Flow.Launcher.Core.Plugin +{ + internal class PluginAssemblyLoader : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver dependencyResolver; + + private readonly AssemblyDependencyResolver referencedPluginPackageDependencyResolver; + + private readonly AssemblyName assemblyName; + + internal PluginAssemblyLoader(string assemblyFilePath) + { + dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath); + assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFilePath)); + + referencedPluginPackageDependencyResolver = + new AssemblyDependencyResolver(Path.Combine(Constant.ProgramDirectory, "Flow.Launcher.Plugin.dll")); + } + + internal Assembly LoadAssemblyAndDependencies() + { + return LoadFromAssemblyName(assemblyName); + } + + protected override Assembly Load(AssemblyName assemblyName) + { + 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; + + return LoadFromAssemblyPath(assemblyPath); + } + + internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type) + { + var allTypes = assembly.ExportedTypes; + + return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(type)); + } + + internal bool ExistsInReferencedPluginPackage(AssemblyName assemblyName) + { + return referencedPluginPackageDependencyResolver.ResolveAssemblyToPath(assemblyName) != null; + } + } +} diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 1025f9bae34..224dbd85e92 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -41,9 +41,9 @@ public static IEnumerable DotNetPlugins(List source) { #if DEBUG - var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath); - var types = assembly.GetTypes(); - var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); + var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); + var assembly = assemblyLoader.LoadAssemblyAndDependencies(); + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin)); var plugin = (IPlugin)Activator.CreateInstance(type); #else Assembly assembly = null; @@ -51,10 +51,10 @@ public static IEnumerable DotNetPlugins(List source) try { - assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath); + var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); + assembly = assemblyLoader.LoadAssemblyAndDependencies(); - var types = assembly.GetTypes(); - var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); + var type = assemblyLoader.FromAssemblyGetTypeOfInterface(assembly, typeof(IPlugin)); plugin = (IPlugin)Activator.CreateInstance(type); } diff --git a/Flow.Launcher.Infrastructure/Alphabet.cs b/Flow.Launcher.Infrastructure/Alphabet.cs deleted file mode 100644 index 7e24a820673..00000000000 --- a/Flow.Launcher.Infrastructure/Alphabet.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using hyjiacan.util.p4n; -using hyjiacan.util.p4n.format; -using JetBrains.Annotations; -using Flow.Launcher.Infrastructure.Logger; -using Flow.Launcher.Infrastructure.Storage; -using Flow.Launcher.Infrastructure.UserSettings; - -namespace Flow.Launcher.Infrastructure -{ - public interface IAlphabet - { - string Translate(string stringToTranslate); - } - - public class Alphabet : IAlphabet - { - private readonly HanyuPinyinOutputFormat Format = new HanyuPinyinOutputFormat(); - private ConcurrentDictionary PinyinCache; - private BinaryStorage> _pinyinStorage; - private Settings _settings; - - public void Initialize([NotNull] Settings settings) - { - _settings = settings ?? throw new ArgumentNullException(nameof(settings)); - InitializePinyinHelpers(); - } - - private void InitializePinyinHelpers() - { - Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); - - Stopwatch.Normal("|Flow Launcher.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () => - { - _pinyinStorage = new BinaryStorage>("Pinyin"); - - lock(_pinyinStorage) - { - var loaded = _pinyinStorage.TryLoad(new Dictionary()); - - PinyinCache = new ConcurrentDictionary(loaded); - } - - // force pinyin library static constructor initialize - PinyinHelper.toHanyuPinyinStringArray('T', Format); - }); - Log.Info($"|Flow Launcher.Infrastructure.Alphabet.Initialize|Number of preload pinyin combination<{PinyinCache.Count}>"); - } - - public string Translate(string str) - { - return ConvertChineseCharactersToPinyin(str); - } - - public string ConvertChineseCharactersToPinyin(string source) - { - if (!_settings.ShouldUsePinyin) - return source; - - if (string.IsNullOrEmpty(source)) - return source; - - if (!ContainsChinese(source)) - return source; - - var combination = PinyinCombination(source); - - var pinyinArray=combination.Select(x => string.Join("", x)); - var acronymArray = combination.Select(Acronym).Distinct(); - - var joinedSingleStringCombination = new StringBuilder(); - var all = acronymArray.Concat(pinyinArray); - all.ToList().ForEach(x => joinedSingleStringCombination.Append(x)); - - return joinedSingleStringCombination.ToString(); - } - - public void Save() - { - if (!_settings.ShouldUsePinyin) - { - return; - } - - lock(_pinyinStorage) - { - _pinyinStorage.Save(PinyinCache.ToDictionary(i => i.Key, i => i.Value)); - } - } - - private static string[] EmptyStringArray = new string[0]; - private static string[][] Empty2DStringArray = new string[0][]; - - /// - /// replace chinese character with pinyin, non chinese character won't be modified - /// Because we don't have words dictionary, so we can only return all possiblie pinyin combination - /// e.g. 音乐 will return yinyue and yinle - /// should be word or sentence, instead of single character. e.g. 微软 - /// - public string[][] PinyinCombination(string characters) - { - if (!_settings.ShouldUsePinyin || string.IsNullOrEmpty(characters)) - { - return Empty2DStringArray; - } - - if (!PinyinCache.ContainsKey(characters)) - { - var allPinyins = new List(); - foreach (var c in characters) - { - var pinyins = PinyinHelper.toHanyuPinyinStringArray(c, Format); - if (pinyins != null) - { - var r = pinyins.Distinct().ToArray(); - allPinyins.Add(r); - } - else - { - var r = new[] { c.ToString() }; - allPinyins.Add(r); - } - } - - var combination = allPinyins.Aggregate(Combination).Select(c => c.Split(';')).ToArray(); - PinyinCache[characters] = combination; - return combination; - } - else - { - return PinyinCache[characters]; - } - } - - public string Acronym(string[] pinyin) - { - var acronym = string.Join("", pinyin.Select(p => p[0])); - return acronym; - } - - public bool ContainsChinese(string word) - { - if (!_settings.ShouldUsePinyin) - { - return false; - } - - if (word.Length > 40) - { - //Skip strings that are too long string for Pinyin conversion. - return false; - } - - var chinese = word.Select(PinyinHelper.toHanyuPinyinStringArray) - .Any(p => p != null); - return chinese; - } - - private string[] Combination(string[] array1, string[] array2) - { - if (!_settings.ShouldUsePinyin) - { - return EmptyStringArray; - } - - var combination = ( - from a1 in array1 - from a2 in array2 - select $"{a1};{a2}" - ).ToArray(); - return combination; - } - } -} diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 410d11536de..28d4c0e1f6a 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,14 +49,11 @@ - - - - + diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 5d7224c5b3d..80c6684f55c 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -2,44 +2,91 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Windows.Media; namespace Flow.Launcher.Infrastructure.Image { [Serializable] + public class ImageUsage + { + + public int usage; + public ImageSource imageSource; + + public ImageUsage(int usage, ImageSource image) + { + this.usage = usage; + imageSource = image; + } + } + public class ImageCache { - private const int MaxCached = 5000; - public ConcurrentDictionary Usage = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _data = new ConcurrentDictionary(); + private const int MaxCached = 50; + public ConcurrentDictionary Data { get; private set; } = new ConcurrentDictionary(); + private const int permissibleFactor = 2; + public void Initialization(Dictionary usage) + { + foreach (var key in usage.Keys) + { + Data[key] = new ImageUsage(usage[key], null); + } + } public ImageSource this[string path] { get { - Usage.AddOrUpdate(path, 1, (k, v) => v + 1); - var i = _data[path]; - return i; + if (Data.TryGetValue(path, out var value)) + { + value.usage++; + return value.imageSource; + } + + return null; } - set { _data[path] = value; } - } + set + { + Data.AddOrUpdate( + path, + new ImageUsage(0, value), + (k, v) => + { + v.imageSource = value; + v.usage++; + return v; + } + ); - public Dictionary CleanupAndToDictionary() - => Usage - .OrderByDescending(o => o.Value) - .Take(MaxCached) - .ToDictionary(i => i.Key, i => i.Value); + // To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size + // This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time + if (Data.Count > permissibleFactor * MaxCached) + { + // To delete the images from the data dictionary based on the resizing of the Usage Dictionary. + + + foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key)) + { + if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon))) + { + Data.TryRemove(key, out _); + } + } + } + } + } public bool ContainsKey(string key) { - var contains = _data.ContainsKey(key); + var contains = Data.ContainsKey(key); return contains; } public int CacheSize() { - return _data.Count; + return Data.Count; } /// @@ -47,8 +94,8 @@ public int CacheSize() /// public int UniqueImagesInCache() { - return _data.Values.Distinct().Count(); + return Data.Values.Select(x => x.imageSource).Distinct().Count(); } } -} +} \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 5cf3d84a61c..edfb88cbfe6 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -13,12 +13,11 @@ namespace Flow.Launcher.Infrastructure.Image { public static class ImageLoader { - private static readonly ImageCache _imageCache = new ImageCache(); - private static readonly ConcurrentDictionary _guidToKey = new ConcurrentDictionary(); - private static readonly bool _enableHashImage = true; - + private static readonly ImageCache ImageCache = new ImageCache(); private static BinaryStorage> _storage; + private static readonly ConcurrentDictionary GuidToKey = new ConcurrentDictionary(); private static IImageHashGenerator _hashGenerator; + private static bool EnableImageHash = true; private static readonly string[] ImageExtensions = { @@ -36,25 +35,25 @@ public static void Initialize() _storage = new BinaryStorage>("Image"); _hashGenerator = new ImageHashGenerator(); - _imageCache.Usage = LoadStorageToConcurrentDictionary(); + var usage = LoadStorageToConcurrentDictionary(); foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) { ImageSource img = new BitmapImage(new Uri(icon)); img.Freeze(); - _imageCache[icon] = img; + ImageCache[icon] = img; } Task.Run(() => { Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () => { - _imageCache.Usage.AsParallel().ForAll(x => + ImageCache.Data.AsParallel().ForAll(x => { Load(x.Key); }); }); - Log.Info($"|ImageLoader.Initialize|Number of preload images is <{_imageCache.Usage.Count}>, Images Number: {_imageCache.CacheSize()}, Unique Items {_imageCache.UniqueImagesInCache()}"); + Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); }); } @@ -62,13 +61,13 @@ public static void Save() { lock (_storage) { - _storage.Save(_imageCache.CleanupAndToDictionary()); + _storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage)); } } private static ConcurrentDictionary LoadStorageToConcurrentDictionary() { - lock(_storage) + lock (_storage) { var loaded = _storage.TryLoad(new Dictionary()); @@ -106,11 +105,11 @@ private static ImageResult LoadInternal(string path, bool loadFullImage = false) { if (string.IsNullOrEmpty(path)) { - return new ImageResult(_imageCache[Constant.MissingImgIcon], ImageType.Error); + return new ImageResult(ImageCache[Constant.MissingImgIcon], ImageType.Error); } - if (_imageCache.ContainsKey(path)) + if (ImageCache.ContainsKey(path)) { - return new ImageResult(_imageCache[path], ImageType.Cache); + return new ImageResult(ImageCache[path], ImageType.Cache); } if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) @@ -139,8 +138,8 @@ private static ImageResult LoadInternal(string path, bool loadFullImage = false) Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e); Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2); - ImageSource image = _imageCache[Constant.MissingImgIcon]; - _imageCache[path] = image; + ImageSource image = ImageCache[Constant.MissingImgIcon]; + ImageCache[path] = image; imageResult = new ImageResult(image, ImageType.Error); } } @@ -191,7 +190,7 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag } else { - image = _imageCache[Constant.MissingImgIcon]; + image = ImageCache[Constant.MissingImgIcon]; path = Constant.MissingImgIcon; } @@ -218,27 +217,26 @@ public static ImageSource Load(string path, bool loadFullImage = false) var img = imageResult.ImageSource; if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache) - { - // we need to get image hash - string hash = _enableHashImage ? _hashGenerator.GetHashFromImage(img) : null; + { // we need to get image hash + string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null; if (hash != null) { - if (_guidToKey.TryGetValue(hash, out string key)) - { - // image already exists - img = _imageCache[key]; + + if (GuidToKey.TryGetValue(hash, out string key)) + { // image already exists + img = ImageCache[key] ?? img; } else - { - // new guid - _guidToKey[hash] = path; + { // new guid + GuidToKey[hash] = path; } } // update cache - _imageCache[path] = img; + ImageCache[path] = img; } + return img; } diff --git a/Flow.Launcher.Infrastructure/PinyinAlphabet.cs b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs new file mode 100644 index 00000000000..38f1ab879c1 --- /dev/null +++ b/Flow.Launcher.Infrastructure/PinyinAlphabet.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using JetBrains.Annotations; +using Flow.Launcher.Infrastructure.Logger; +using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Infrastructure.UserSettings; +using ToolGood.Words.Pinyin; +using System.Threading.Tasks; + +namespace Flow.Launcher.Infrastructure +{ + public interface IAlphabet + { + string Translate(string stringToTranslate); + } + + public class PinyinAlphabet : IAlphabet + { + private ConcurrentDictionary _pinyinCache = new ConcurrentDictionary(); + private Settings _settings; + + public void Initialize([NotNull] Settings settings) + { + _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + } + + + public string Translate(string content) + { + if (_settings.ShouldUsePinyin) + { + if (!_pinyinCache.ContainsKey(content)) + { + if (WordsHelper.HasChinese(content)) + { + var result = WordsHelper.GetPinyin(content, ";"); + result = GetFirstPinyinChar(result) + result.Replace(";", ""); + _pinyinCache[content] = result; + return result; + } + else + { + return content; + } + } + else + { + return _pinyinCache[content]; + } + } + else + { + return content; + } + } + + private string GetFirstPinyinChar(string content) + { + return string.Concat(content.Split(';').Select(x => x.First())); + } + } +} \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 5dda76bc4d7..c5ab7dd3b7c 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -14,10 +14,10 @@ - 1.2.1 - 1.2.1 - 1.2.1 - 1.2.1 + 1.2.2 + 1.2.2 + 1.2.2 + 1.2.2 Flow.Launcher.Plugin Flow-Launcher MIT @@ -60,12 +60,9 @@ - + - - - \ No newline at end of file diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index 1dd93b2ba38..6196aa5df1f 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -53,6 +53,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitattributes = .gitattributes .gitignore = .gitignore appveyor.yml = appveyor.yml + Directory.Build.targets = Directory.Build.targets Scripts\flowlauncher.nuspec = Scripts\flowlauncher.nuspec LICENSE = LICENSE Scripts\post_build.ps1 = Scripts\post_build.ps1 diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 731dc15416d..59bdbc8960f 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -29,7 +29,7 @@ public partial class App : IDisposable, ISingleInstanceApp private SettingWindowViewModel _settingsVM; private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo); private readonly Portable _portable = new Portable(); - private readonly Alphabet _alphabet = new Alphabet(); + private readonly PinyinAlphabet _alphabet = new PinyinAlphabet(); private StringMatcher _stringMatcher; [STAThread] diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 987a685ac54..8548ba39e5f 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -72,23 +72,13 @@ - - - - all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 23f5d85b704..0cc5a0e5d1f 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -21,11 +21,11 @@ public class PublicAPIInstance : IPublicAPI { private readonly SettingWindowViewModel _settingsVM; private readonly MainViewModel _mainVM; - private readonly Alphabet _alphabet; + private readonly PinyinAlphabet _alphabet; #region Constructor - public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, Alphabet alphabet) + public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, PinyinAlphabet alphabet) { _settingsVM = settingsVM; _mainVM = mainVM; @@ -76,7 +76,6 @@ public void SaveAppAllSettings() _settingsVM.Save(); PluginManager.Save(); ImageLoader.Save(); - _alphabet.Save(); } public void ReloadAllPluginData() diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs index 1149042ddce..790c03686d0 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs @@ -19,10 +19,8 @@ public string Name set { m_Name = value; - PinyinName = m_Name.Unidecode(); } } - public string PinyinName { get; private set; } public string Url { get; set; } public string Source { get; set; } public int Score { get; set; } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs index 7c2db8bf9a5..c7013aa677b 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Commands/Bookmarks.cs @@ -6,19 +6,19 @@ namespace Flow.Launcher.Plugin.BrowserBookmark.Commands { internal static class Bookmarks { - internal static bool MatchProgram(Bookmark bookmark, string queryString) + internal static MatchResult MatchProgram(Bookmark bookmark, string queryString) { - if (StringMatcher.FuzzySearch(queryString, bookmark.Name).IsSearchPrecisionScoreMet()) return true; - if (StringMatcher.FuzzySearch(queryString, bookmark.PinyinName).IsSearchPrecisionScoreMet()) return true; - if (StringMatcher.FuzzySearch(queryString, bookmark.Url).IsSearchPrecisionScoreMet()) return true; + var match = StringMatcher.FuzzySearch(queryString, bookmark.Name); + if (match.IsSearchPrecisionScoreMet()) + return match; - return false; + return StringMatcher.FuzzySearch(queryString, bookmark.Url); } internal static List LoadAllBookmarks() { var allbookmarks = new List(); - + var chromeBookmarks = new ChromeBookmarks(); var mozBookmarks = new FirefoxBookmarks(); var edgeBookmarks = new EdgeBookmarks(); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs index 12b80c08ad7..37680854949 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs @@ -18,7 +18,7 @@ public List GetBookmarks() return bookmarks; } - private void ParseEdgeBookmarks(String path, string source) + private void ParseEdgeBookmarks(string path, string source) { if (!File.Exists(path)) return; @@ -72,12 +72,13 @@ private void LoadEdgeBookmarks(string path, string name) private void LoadEdgeBookmarks() { - String platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge"); + LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev"); LoadEdgeBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary"); } - private String DecodeUnicode(String dataStr) + private string DecodeUnicode(string dataStr) { Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})"); return reg.Replace(dataStr, m => ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString()); 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 13daddf109b..85b745a6b83 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {9B130CC5-14FB-41FF-B310-0A95B6894C37} Properties Flow.Launcher.Plugin.BrowserBookmark Flow.Launcher.Plugin.BrowserBookmark + true false false diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 67ee87f9428..47493654f38 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -12,7 +12,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, ISavable { private PluginInitContext context; - + private List cachedBookmarks = new List(); private readonly Settings _settings; @@ -37,36 +37,56 @@ public List Query(Query query) // Should top results be returned? (true if no search parameters have been passed) var topResults = string.IsNullOrEmpty(param); - - var returnList = cachedBookmarks; + if (!topResults) { // Since we mixed chrome and firefox bookmarks, we should order them again - returnList = cachedBookmarks.Where(o => Bookmarks.MatchProgram(o, param)).ToList(); - returnList = returnList.OrderByDescending(o => o.Score).ToList(); - } - - return returnList.Select(c => new Result() - { - Title = c.Name, - SubTitle = c.Url, - IcoPath = @"Images\bookmark.png", - Score = 5, - Action = (e) => + var returnList = cachedBookmarks.Select(c => new Result() { - if (_settings.OpenInNewBrowserWindow) + Title = c.Name, + SubTitle = c.Url, + IcoPath = @"Images\bookmark.png", + Score = Bookmarks.MatchProgram(c, param).Score, + Action = _ => { - c.Url.NewBrowserWindow(_settings.BrowserPath); + if (_settings.OpenInNewBrowserWindow) + { + c.Url.NewBrowserWindow(_settings.BrowserPath); + } + else + { + c.Url.NewTabInBrowser(_settings.BrowserPath); + } + + return true; } - else + }).Where(r => r.Score > 0); + return returnList.ToList(); + } + else + { + return cachedBookmarks.Select(c => new Result() + { + Title = c.Name, + SubTitle = c.Url, + IcoPath = @"Images\bookmark.png", + Score = 5, + Action = _ => { - c.Url.NewTabInBrowser(_settings.BrowserPath); - } + if (_settings.OpenInNewBrowserWindow) + { + c.Url.NewBrowserWindow(_settings.BrowserPath); + } + else + { + c.Url.NewTabInBrowser(_settings.BrowserPath); + } - return true; - } - }).ToList(); + return true; + } + }).ToList(); + } } public void ReloadData() diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json index 8676a3e5bfb..98db163ec86 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json @@ -4,9 +4,9 @@ "Name": "Browser Bookmarks", "Description": "Search your browser bookmarks", "Author": "qianlifeng, Ioannis G.", - "Version": "1.2.0", + "Version": "1.2.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", - "ExecuteFileName": "Flow.Launcher.Plugin.browserBookmark.dll", + "ExecuteFileName": "Flow.Launcher.Plugin.BrowserBookmark.dll", "IcoPath": "Images\\bookmark.png" } 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 e7cae42aebe..9e1fefdb30d 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -1,12 +1,14 @@ - + + Library netcoreapp3.1 {59BD9891-3837-438A-958D-ADC7F91F6F7E} Properties Flow.Launcher.Plugin.Caculator Flow.Launcher.Plugin.Caculator - true + true + true false false @@ -102,9 +104,7 @@ - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json b/Plugins/Flow.Launcher.Plugin.Calculator/plugin.json index 31678888447..16a46f4e50a 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.0.1", + "Version": "1.0.2", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Caculator.dll", diff --git a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj index 19f8fb98075..c7fe8271a6c 100644 --- a/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj +++ b/Plugins/Flow.Launcher.Plugin.Color/Flow.Launcher.Plugin.Color.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {F35190AA-4758-4D9E-A193-E3BDF6AD3567} Properties Flow.Launcher.Plugin.Color Flow.Launcher.Plugin.Color + true false false @@ -96,9 +98,4 @@ - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Color/plugin.json b/Plugins/Flow.Launcher.Plugin.Color/plugin.json index 938f8f8535b..dbba029c6c5 100644 --- a/Plugins/Flow.Launcher.Plugin.Color/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Color/plugin.json @@ -4,7 +4,7 @@ "Name": "Colors", "Description": "Provide hex color preview.(Try #000 in Flow Launcher)", "Author": "qianlifeng", - "Version": "1.0.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Color.dll", 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 d1c185c3633..69973763435 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/Flow.Launcher.Plugin.ControlPanel.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {1EE20B48-82FB-48A2-8086-675D6DDAB4F0} Properties Flow.Launcher.Plugin.ControlPanel Flow.Launcher.Plugin.ControlPanel + true false false @@ -96,9 +98,4 @@ - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json b/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json index 9a4b7ee0e50..d7940125ee9 100644 --- a/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.ControlPanel/plugin.json @@ -4,7 +4,7 @@ "Name": "Control Panel", "Description": "Search within the Control Panel.", "Author": "CoenraadS", - "Version": "1.0.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.ControlPanel.dll", 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 efa5339b4d9..a1a08843a50 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -5,6 +5,7 @@ netcoreapp3.1 true true + true false diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json b/Plugins/Flow.Launcher.Plugin.Explorer/plugin.json index 7c6ef82b04c..2c57ac668e8 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.2.4", + "Version": "1.2.5", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Explorer.dll", 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 48639156e69..e6bfa7aa396 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -1,11 +1,13 @@  + Library netcoreapp3.1 {FDED22C8-B637-42E8-824A-63B5B6E05A3A} Properties Flow.Launcher.Plugin.PluginIndicator Flow.Launcher.Plugin.PluginIndicator + true false false @@ -96,10 +98,5 @@ PreserveNewest - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json index 0c1cce6eb9d..7b2f66989a5 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json @@ -4,7 +4,7 @@ "Name": "Plugin Indicator", "Description": "Provide plugin actionword suggestion", "Author": "qianlifeng", - "Version": "1.0.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginIndicator.dll", diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj index 49451d5ba84..08e89d86125 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginManagement/Flow.Launcher.Plugin.PluginManagement.csproj @@ -1,12 +1,14 @@  + Library netcoreapp3.1 {049490F0-ECD2-4148-9B39-2135EC346EBE} Properties Flow.Launcher.Plugin.PluginManagement Flow.Launcher.Plugin.PluginManagement true + true false false @@ -97,10 +99,4 @@ - - - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json b/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json index 5a8259f8fe0..3d73c219787 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.PluginManagement/plugin.json @@ -4,7 +4,7 @@ "Name": "Plugin Management", "Description": "Install/Remove/Update Flow Launcher plugins", "Author": "qianlifeng", - "Version": "1.0", + "Version": "1.0.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.PluginManagement.dll", 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 ab84aa54acf..cf9c9629402 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -1,6 +1,7 @@  + Library netcoreapp3.1 Flow.Launcher.Plugin.ProcessKiller Flow.Launcher.Plugin.ProcessKiller @@ -8,6 +9,7 @@ https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller flow-launcher flow-plugin + true false false diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json index 894c49bdf6c..3e7ba404342 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json @@ -4,7 +4,7 @@ "Name":"Process Killer", "Description":"kill running processes from Flow", "Author":"Flow-Launcher", - "Version":"1.1.0", + "Version":"1.1.1", "Language":"csharp", "Website":"https://github.com/Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller", "IcoPath":"Images\\app.png", 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 331566f90d1..3802297c70a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -1,6 +1,7 @@  + Library netcoreapp3.1 {FDB3555B-58EF-4AE6-B5F1-904719637AB4} Properties @@ -8,6 +9,7 @@ Flow.Launcher.Plugin.Program true true + true false false @@ -107,10 +109,7 @@ - - - diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index 579a64718d8..c266aa6f68d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -40,7 +40,12 @@ Search programs in Flow Launcher Invalid Path - + + Customized Explorer + Args + You can customized the explorer used for opening the container folder by inputing the Environmental Variable of the explorer you want to use. It will be useful to use CMD to test whether the Environmental Variable is avaliable. + Enter the customized args you want to add for your customized explorer. %s for parent directory, %f for full path (which only works for win32). Check the explorer's website for details. + Success Successfully disabled this program from displaying in your query diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 9f3160746b8..8f124f3a40b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -71,21 +71,17 @@ public List Query(Query query) Win32[] win32; UWP.Application[] uwps; - lock (IndexLock) - { // just take the reference inside the lock to eliminate query time issues. - win32 = _win32s; - uwps = _uwps; - } - - var results1 = win32.AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)); + win32 = _win32s; + uwps = _uwps; - var results2 = uwps.AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)); + var result = win32.Cast() + .Concat(uwps) + .AsParallel() + .Where(p => p.Enabled) + .Select(p => p.Result(query.Search, _context.API)) + .Where(r => r?.Score > 0) + .ToList(); - var result = results1.Concat(results2).Where(r => r != null && r.Score > 0).ToList(); return result; } @@ -97,10 +93,9 @@ public void Init(PluginInitContext context) public static void IndexWin32Programs() { var win32S = Win32.All(_settings); - lock (IndexLock) - { - _win32s = win32S; - } + + _win32s = win32S; + } public static void IndexUWPPrograms() @@ -109,10 +104,9 @@ public static void IndexUWPPrograms() var support = Environment.OSVersion.Version.Major >= windows10.Major; var applications = support ? UWP.All() : new UWP.Application[] { }; - lock (IndexLock) - { - _uwps = applications; - } + + _uwps = applications; + } public static void IndexPrograms() diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs index b42acfbce5b..d4c96e5b730 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs @@ -9,5 +9,6 @@ public interface IProgram string UniqueIdentifier { get; set; } string Name { get; } string Location { get; } + bool Enabled { get; } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 69e077ee2f6..3ea78156d77 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -265,27 +265,30 @@ public class Application : IProgram public Application(){} - private int Score(string query) - { - var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName); - var descriptionMatch = StringMatcher.FuzzySearch(query, Description); - var score = new[] { displayNameMatch.Score, descriptionMatch.Score }.Max(); - return score; - } public Result Result(string query, IPublicAPI api) { - var score = Score(query); - if (score <= 0) - { // no need to create result if score is 0 + var title = (Name, Description) switch + { + (var n, null) => n, + (var n, var d) when d.StartsWith(n) => d, + (var n, var d) when n.StartsWith(d) => n, + (var n, var d) when !string.IsNullOrEmpty(d) => $"{n}: {d}", + _ => Name + }; + + var matchResult = StringMatcher.FuzzySearch(query, title); + + if (!matchResult.Success) return null; - } var result = new Result { + Title = title, SubTitle = Package.Location, Icon = Logo, - Score = score, + Score = matchResult.Score, + TitleHighlightData = matchResult.MatchData, ContextData = this, Action = e => { @@ -294,23 +297,7 @@ public Result Result(string query, IPublicAPI api) } }; - if (Description.Length >= DisplayName.Length && - Description.Substring(0, DisplayName.Length) == DisplayName) - { - result.Title = Description; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData; - } - else if (!string.IsNullOrEmpty(Description)) - { - var title = $"{DisplayName}: {Description}"; - result.Title = title; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData; - } - else - { - result.Title = DisplayName; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, DisplayName).MatchData; - } + return result; } @@ -324,7 +311,14 @@ public List ContextMenus(IPublicAPI api) Action = _ => { - Main.StartProcess(Process.Start, new ProcessStartInfo(Package.Location)); + Main.StartProcess(Process.Start, + new ProcessStartInfo( + !string.IsNullOrEmpty(Main._settings.CustomizedExplorer) + ? Main._settings.CustomizedExplorer + : Settings.Explorer, + Main._settings.CustomizedArgs + .Replace("%s",$"\"{Package.Location}\"") + .Trim())); return true; }, diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index cdea767f3ee..092418b6c40 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -33,29 +33,30 @@ public class Win32 : IProgram private const string ApplicationReferenceExtension = "appref-ms"; private const string ExeExtension = "exe"; - private int Score(string query) - { - var nameMatch = StringMatcher.FuzzySearch(query, Name); - var descriptionMatch = StringMatcher.FuzzySearch(query, Description); - var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName); - var score = new[] { nameMatch.Score, descriptionMatch.Score, executableNameMatch.Score }.Max(); - return score; - } - public Result Result(string query, IPublicAPI api) { - var score = Score(query); - if (score <= 0) - { // no need to create result if this is zero + var title = (Name, Description) switch + { + (var n, null) => n, + (var n, var d) when d.StartsWith(n) => d, + (var n, var d) when n.StartsWith(d) => n, + (var n, var d) when !string.IsNullOrEmpty(d) => $"{n}: {d}", + _ => Name + }; + + var matchResult = StringMatcher.FuzzySearch(query, title); + + if (!matchResult.Success) return null; - } var result = new Result { + Title = title, SubTitle = FullPath, IcoPath = IcoPath, - Score = score, + Score = matchResult.Score, + TitleHighlightData = matchResult.MatchData, ContextData = this, Action = e => { @@ -72,24 +73,6 @@ public Result Result(string query, IPublicAPI api) } }; - if (Description.Length >= Name.Length && - Description.Substring(0, Name.Length) == Name) - { - result.Title = Description; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData; - } - else if (!string.IsNullOrEmpty(Description)) - { - var title = $"{Name}: {Description}"; - result.Title = title; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData; - } - else - { - result.Title = Name; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData; - } - return result; } @@ -140,9 +123,20 @@ public List ContextMenus(IPublicAPI api) Title = api.GetTranslation("flowlauncher_plugin_program_open_containing_folder"), Action = _ => { - - - Main.StartProcess(Process.Start, new ProcessStartInfo("explorer", ParentDirectory)); + 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.StartProcess(Process.Start, + new ProcessStartInfo( + !string.IsNullOrWhiteSpace(Main._settings.CustomizedExplorer) + ? Main._settings.CustomizedExplorer + : Settings.Explorer, + args)); return true; }, @@ -255,9 +249,7 @@ private static Win32 ExeProgram(string path) var program = Win32Program(path); var info = FileVersionInfo.GetVersionInfo(path); if (!string.IsNullOrEmpty(info.FileDescription)) - { program.Description = info.FileDescription; - } return program; } catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) @@ -273,47 +265,23 @@ private static IEnumerable ProgramPaths(string directory, string[] suffi { if (!Directory.Exists(directory)) return new string[] { }; - var files = new List(); - var folderQueue = new Queue(); - folderQueue.Enqueue(directory); - do + try { - var currentDirectory = folderQueue.Dequeue(); - try - { - foreach (var suffix in suffixes) - { - try - { - files.AddRange(Directory.EnumerateFiles(currentDirectory, $"*.{suffix}", SearchOption.TopDirectoryOnly)); - } - catch (DirectoryNotFoundException e) - { - ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" + - "|The directory trying to load the program from does not exist", e); - } - } - } - catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) - { - ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" + - $"|Permission denied when trying to load programs from {currentDirectory}", e); - } + var paths = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories) + .Where(x => suffixes.Contains(Extension(x))); + return paths; - try - { - foreach (var childDirectory in Directory.EnumerateDirectories(currentDirectory, "*", SearchOption.TopDirectoryOnly)) - { - folderQueue.Enqueue(childDirectory); - } - } - catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) - { - ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" + - $"|Permission denied when trying to load programs from {currentDirectory}", e); - } - } while (folderQueue.Any()); - return files; + } + catch (DirectoryNotFoundException e) + { + 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[] { }; + } } private static string Extension(string path) @@ -331,23 +299,20 @@ private static string Extension(string path) private static ParallelQuery UnregisteredPrograms(List sources, string[] suffixes) { - var listToAdd = new List(); - sources.Where(s => Directory.Exists(s.Location) && s.Enabled) + var paths = sources.Where(s => Directory.Exists(s.Location) && s.Enabled) .SelectMany(s => ProgramPaths(s.Location, suffixes)) - .ToList() .Where(t1 => !Main._settings.DisabledProgramSources.Any(x => t1 == x.UniqueIdentifier)) - .ToList() - .ForEach(x => listToAdd.Add(x)); - - var paths = listToAdd.Distinct().ToArray(); - - var programs1 = paths.AsParallel().Where(p => Extension(p) == ExeExtension).Select(ExeProgram); - var programs2 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram); - var programs3 = from p in paths.AsParallel() - let e = Extension(p) - where e != ShortcutExtension && e != ExeExtension - select Win32Program(p); - return programs1.Concat(programs2).Concat(programs3); + .Distinct(); + + var programs = paths.AsParallel().Select(x => Extension(x) switch + { + ExeExtension => ExeProgram(x), + ShortcutExtension => LnkProgram(x), + _ => Win32Program(x) + }); + + + return programs; } private static ParallelQuery StartMenuPrograms(string[] suffixes) @@ -360,15 +325,16 @@ private static ParallelQuery StartMenuPrograms(string[] suffixes) var paths2 = ProgramPaths(directory2, suffixes); var toFilter = paths1.Concat(paths2); - var paths = toFilter + + var programs = toFilter + .AsParallel() .Where(t1 => !disabledProgramsList.Any(x => x.UniqueIdentifier == t1)) - .Select(t1 => t1) .Distinct() - .ToArray(); - - var programs1 = paths.AsParallel().Where(p => Extension(p) == ShortcutExtension).Select(LnkProgram); - var programs2 = paths.AsParallel().Where(p => Extension(p) == ApplicationReferenceExtension).Select(Win32Program); - var programs = programs1.Concat(programs2).Where(p => p.Valid); + .Select(x => Extension(x) switch + { + ShortcutExtension => LnkProgram(x), + _ => Win32Program(x) + }).Where(x => x.Valid); return programs; } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs index fcb4cbf2da6..7cb02a85258 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs @@ -14,9 +14,15 @@ public class Settings public bool EnableStartMenuSource { get; set; } = true; public bool EnableRegistrySource { get; set; } = true; + public string CustomizedExplorer { get; set; } = Explorer; + public string CustomizedArgs { get; set; } = ExplorerArgs; internal const char SuffixSeperator = ';'; + internal const string Explorer = "explorer"; + + internal const string ExplorerArgs = "%s"; + /// /// Contains user added folder location contents as well as all user disabled applications /// diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml index 6051e0579fc..90fda1756a3 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml @@ -5,12 +5,13 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:program="clr-namespace:Flow.Launcher.Plugin.Program" mc:Ignorable="d" - d:DesignHeight="300" d:DesignWidth="600"> + d:DesignHeight="404.508" d:DesignWidth="600"> + @@ -24,7 +25,7 @@ - + @@ -71,9 +72,18 @@