diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..4596aa084 --- /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 87c390d34..9f146a457 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 000000000..b9b878a7b --- /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 1025f9bae..224dbd85e 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -41,9 +41,9 @@ namespace Flow.Launcher.Core.Plugin { #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 @@ namespace Flow.Launcher.Core.Plugin 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 7e24a8206..000000000 --- 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 410d11536..28d4c0e1f 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 5d7224c5b..80c6684f5 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -2,44 +2,91 @@ using System; 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 @@ namespace Flow.Launcher.Infrastructure.Image /// 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 5cf3d84a6..edfb88cbf 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 @@ namespace Flow.Launcher.Infrastructure.Image _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 @@ namespace Flow.Launcher.Infrastructure.Image { 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 @@ namespace Flow.Launcher.Infrastructure.Image { 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 @@ namespace Flow.Launcher.Infrastructure.Image 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 @@ namespace Flow.Launcher.Infrastructure.Image } else { - image = _imageCache[Constant.MissingImgIcon]; + image = ImageCache[Constant.MissingImgIcon]; path = Constant.MissingImgIcon; } @@ -218,27 +217,26 @@ namespace Flow.Launcher.Infrastructure.Image 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 000000000..38f1ab879 --- /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 5dda76bc4..c5ab7dd3b 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 1dd93b2ba..6196aa5df 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 731dc1541..59bdbc896 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -29,7 +29,7 @@ namespace Flow.Launcher 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 987a685ac..8548ba39e 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 23f5d85b7..0cc5a0e5d 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -21,11 +21,11 @@ namespace Flow.Launcher { 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 @@ namespace Flow.Launcher _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 1149042dd..790c03686 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Bookmark.cs @@ -19,10 +19,8 @@ namespace Flow.Launcher.Plugin.BrowserBookmark 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 7c2db8bf9..c7013aa67 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 12b80c08a..376808549 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarks.cs @@ -18,7 +18,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark 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 @@ namespace Flow.Launcher.Plugin.BrowserBookmark 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 13daddf10..85b745a6b 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 67ee87f94..47493654f 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 @@ namespace Flow.Launcher.Plugin.BrowserBookmark // 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); - } - else - { - c.Url.NewTabInBrowser(_settings.BrowserPath); - } + if (_settings.OpenInNewBrowserWindow) + { + c.Url.NewBrowserWindow(_settings.BrowserPath); + } + else + { + c.Url.NewTabInBrowser(_settings.BrowserPath); + } - return true; - } - }).ToList(); + return true; + } + }).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 = _ => + { + if (_settings.OpenInNewBrowserWindow) + { + c.Url.NewBrowserWindow(_settings.BrowserPath); + } + else + { + c.Url.NewTabInBrowser(_settings.BrowserPath); + } + + 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 8676a3e5b..98db163ec 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 e7cae42ae..9e1fefdb3 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 316788884..16a46f4e5 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 19f8fb980..c7fe8271a 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 938f8f853..dbba029c6 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 d1c185c36..699737634 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 9a4b7ee0e..d7940125e 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 efa5339b4..a1a08843a 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 7c6ef82b0..2c57ac668 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 48639156e..e6bfa7aa3 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 0c1cce6eb..7b2f66989 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 49451d5ba..08e89d861 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 5a8259f8f..3d73c2197 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 ab84aa54a..cf9c96294 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 894c49bdf..3e7ba4043 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 331566f90..3802297c7 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 579a64718..c266aa6f6 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 9f3160746..8f124f3a4 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -71,21 +71,17 @@ namespace Flow.Launcher.Plugin.Program Win32[] win32; UWP.Application[] uwps; - lock (IndexLock) - { // just take the reference inside the lock to eliminate query time issues. - win32 = _win32s; - uwps = _uwps; - } + win32 = _win32s; + uwps = _uwps; - var results1 = win32.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 results2 = uwps.AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)); - - var result = results1.Concat(results2).Where(r => r != null && r.Score > 0).ToList(); return result; } @@ -97,10 +93,9 @@ namespace Flow.Launcher.Plugin.Program public static void IndexWin32Programs() { var win32S = Win32.All(_settings); - lock (IndexLock) - { - _win32s = win32S; - } + + _win32s = win32S; + } public static void IndexUWPPrograms() @@ -109,10 +104,9 @@ namespace Flow.Launcher.Plugin.Program 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 b42acfbce..d4c96e5b7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/IProgram.cs @@ -9,5 +9,6 @@ namespace Flow.Launcher.Plugin.Program.Programs 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 69e077ee2..3ea78156d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -265,27 +265,30 @@ namespace Flow.Launcher.Plugin.Program.Programs 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 @@ namespace Flow.Launcher.Plugin.Program.Programs } }; - 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 @@ namespace Flow.Launcher.Plugin.Program.Programs 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 cdea767f3..092418b6c 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -33,29 +33,30 @@ namespace Flow.Launcher.Plugin.Program.Programs 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 @@ namespace Flow.Launcher.Plugin.Program.Programs } }; - 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 @@ namespace Flow.Launcher.Plugin.Program.Programs Title = api.GetTranslation("flowlauncher_plugin_program_open_containing_folder"), 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.StartProcess(Process.Start, new ProcessStartInfo("explorer", ParentDirectory)); + Main.StartProcess(Process.Start, + new ProcessStartInfo( + !string.IsNullOrWhiteSpace(Main._settings.CustomizedExplorer) + ? Main._settings.CustomizedExplorer + : Settings.Explorer, + args)); return true; }, @@ -255,9 +249,7 @@ namespace Flow.Launcher.Plugin.Program.Programs 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 @@ namespace Flow.Launcher.Plugin.Program.Programs { 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 @@ namespace Flow.Launcher.Plugin.Program.Programs 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)); + .Distinct(); - var paths = listToAdd.Distinct().ToArray(); + var programs = paths.AsParallel().Select(x => Extension(x) switch + { + ExeExtension => ExeProgram(x), + ShortcutExtension => LnkProgram(x), + _ => Win32Program(x) + }); - 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); + + return programs; } private static ParallelQuery StartMenuPrograms(string[] suffixes) @@ -360,15 +325,16 @@ namespace Flow.Launcher.Plugin.Program.Programs var paths2 = ProgramPaths(directory2, suffixes); var toFilter = paths1.Concat(paths2); - var paths = toFilter - .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); + var programs = toFilter + .AsParallel() + .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; } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs index fcb4cbf2d..7cb02a852 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Settings.cs @@ -14,9 +14,15 @@ namespace Flow.Launcher.Plugin.Program 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 6051e0579..90fda1756 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 @@