diff --git a/Plugins/Wox.Plugin.CMD/CMD.cs b/Plugins/Wox.Plugin.CMD/CMD.cs index 0cd255bc37..7b100dc6bb 100644 --- a/Plugins/Wox.Plugin.CMD/CMD.cs +++ b/Plugins/Wox.Plugin.CMD/CMD.cs @@ -21,22 +21,17 @@ namespace Wox.Plugin.CMD public List Query(Query query) { List results = new List(); - List pushedResults = new List(); string cmd = query.Search; if (string.IsNullOrEmpty(cmd)) { - return GetAllHistoryCmds(); + return ResultsFromlHistory(); } else { var queryCmd = GetCurrentCmd(cmd); - context.API.PushResults(query, context.CurrentPluginMetadata, new List() { queryCmd }); - pushedResults.Add(queryCmd); - + results.Add(queryCmd); var history = GetHistoryCmds(cmd, queryCmd); - context.API.PushResults(query, context.CurrentPluginMetadata, history); - pushedResults.AddRange(history); - + results.AddRange(history); try { @@ -57,7 +52,11 @@ namespace Wox.Plugin.CMD if (basedir != null) { - List autocomplete = Directory.GetFileSystemEntries(basedir).Select(o => dir + Path.GetFileName(o)).Where(o => o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) && !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) && !pushedResults.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList(); + var autocomplete = Directory.GetFileSystemEntries(basedir). + Select(o => dir + Path.GetFileName(o)). + Where(o => o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) && + !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) && + !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList(); autocomplete.Sort(); results.AddRange(autocomplete.ConvertAll(m => new Result() { @@ -94,7 +93,7 @@ namespace Wox.Plugin.CMD var ret = new Result { Title = m.Key, - SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value), + SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value), IcoPath = "Images/cmd.png", Action = (c) => { @@ -125,13 +124,13 @@ namespace Wox.Plugin.CMD return result; } - private List GetAllHistoryCmds() + private List ResultsFromlHistory() { IEnumerable history = CMDStorage.Instance.CMDHistory.OrderByDescending(o => o.Value) .Select(m => new Result { Title = m.Key, - SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value), + SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value), IcoPath = "Images/cmd.png", Action = (c) => { diff --git a/Plugins/Wox.Plugin.PluginIndicator/PluginIndicator.cs b/Plugins/Wox.Plugin.PluginIndicator/PluginIndicator.cs index f7b2bd0314..f7f518889c 100644 --- a/Plugins/Wox.Plugin.PluginIndicator/PluginIndicator.cs +++ b/Plugins/Wox.Plugin.PluginIndicator/PluginIndicator.cs @@ -13,10 +13,9 @@ namespace Wox.Plugin.PluginIndicator public List Query(Query query) { - var results = from plugin in PluginManager.NonGlobalPlugins - select plugin.Metadata into metadata - from keyword in metadata.ActionKeywords + var results = from keyword in PluginManager.NonGlobalPlugins.Keys where keyword.StartsWith(query.Terms[0]) + let metadata = PluginManager.NonGlobalPlugins[keyword].Metadata let customizedPluginConfig = UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == metadata.ID) where customizedPluginConfig == null || !customizedPluginConfig.Disabled diff --git a/Plugins/Wox.Plugin.Program/FileChangeWatcher.cs b/Plugins/Wox.Plugin.Program/FileChangeWatcher.cs index 4d258e74fc..cb3b315aa6 100644 --- a/Plugins/Wox.Plugin.Program/FileChangeWatcher.cs +++ b/Plugins/Wox.Plugin.Program/FileChangeWatcher.cs @@ -2,7 +2,7 @@ using System.Diagnostics; using System.IO; using System.Threading; - +using Wox.Infrastructure.Logger; namespace Wox.Plugin.Program { internal class FileChangeWatcher @@ -15,7 +15,7 @@ namespace Wox.Plugin.Program if (watchedPath.Contains(path)) return; if (!Directory.Exists(path)) { - Debug.WriteLine(string.Format("FileChangeWatcher: {0} doesn't exist", path)); + Log.Warn($"FileChangeWatcher: {path} doesn't exist"); return; } diff --git a/Plugins/Wox.Plugin.Program/ProgramSources/AppPathsProgramSource.cs b/Plugins/Wox.Plugin.Program/ProgramSources/AppPathsProgramSource.cs index 16e0961b3d..817f1e174f 100644 --- a/Plugins/Wox.Plugin.Program/ProgramSources/AppPathsProgramSource.cs +++ b/Plugins/Wox.Plugin.Program/ProgramSources/AppPathsProgramSource.cs @@ -57,7 +57,7 @@ namespace Wox.Plugin.Program.ProgramSources } catch (Exception e) { - Log.Error(e.StackTrace); + Log.Error(e); } } } diff --git a/Plugins/Wox.Plugin.Program/ProgramSources/FileSystemProgramSource.cs b/Plugins/Wox.Plugin.Program/ProgramSources/FileSystemProgramSource.cs index cb0cf7e765..f5571a18df 100644 --- a/Plugins/Wox.Plugin.Program/ProgramSources/FileSystemProgramSource.cs +++ b/Plugins/Wox.Plugin.Program/ProgramSources/FileSystemProgramSource.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Wox.Core.Exception; using Wox.Infrastructure.Logger; namespace Wox.Plugin.Program.ProgramSources @@ -70,7 +71,8 @@ namespace Wox.Plugin.Program.ProgramSources } catch (Exception e) { - Log.Warn(string.Format("GetAppFromDirectory failed: {0} - {1}", path, e.Message)); + var woxPluginException = new WoxPluginException("Program", $"GetAppFromDirectory failed: {path}", e); + Log.Error(woxPluginException); } } diff --git a/Plugins/Wox.Plugin.Program/Programs.cs b/Plugins/Wox.Plugin.Program/Programs.cs index 7f98198644..8ec1ea9a30 100644 --- a/Plugins/Wox.Plugin.Program/Programs.cs +++ b/Plugins/Wox.Plugin.Program/Programs.cs @@ -8,6 +8,7 @@ using System.Windows; using IWshRuntimeLibrary; using Wox.Infrastructure; using Wox.Plugin.Program.ProgramSources; +using Wox.Infrastructure.Logger; using Stopwatch = Wox.Infrastructure.Stopwatch; namespace Wox.Plugin.Program @@ -17,7 +18,7 @@ namespace Wox.Plugin.Program private static object lockObject = new object(); private static List programs = new List(); private static List sources = new List(); - private static Dictionary SourceTypes = new Dictionary() { + private static Dictionary SourceTypes = new Dictionary() { {"FileSystemProgramSource", typeof(FileSystemProgramSource)}, {"CommonStartMenuProgramSource", typeof(CommonStartMenuProgramSource)}, {"UserStartMenuProgramSource", typeof(UserStartMenuProgramSource)}, @@ -27,7 +28,7 @@ namespace Wox.Plugin.Program public List Query(Query query) { - + var fuzzyMather = FuzzyMatcher.Create(query.Search); List returnList = programs.Where(o => MatchProgram(o, fuzzyMather)).ToList(); returnList.ForEach(ScoreFilter); @@ -75,7 +76,7 @@ namespace Wox.Plugin.Program { programs = ProgramCacheStorage.Instance.Programs; }); - Debug.WriteLine($"Preload {programs.Count} programs from cache"); + Log.Info($"Preload {programs.Count} programs from cache"); Stopwatch.Debug("Program Index", IndexPrograms); } @@ -98,7 +99,7 @@ namespace Wox.Plugin.Program } sources.Clear(); - foreach(var source in programSources.Where(o => o.Enabled)) + foreach (var source in programSources.Where(o => o.Enabled)) { Type sourceClass; if (SourceTypes.TryGetValue(source.Type, out sourceClass)) diff --git a/Plugins/Wox.Plugin.WebSearch/EasyTimer.cs b/Plugins/Wox.Plugin.WebSearch/EasyTimer.cs deleted file mode 100644 index 49c31e63d3..0000000000 --- a/Plugins/Wox.Plugin.WebSearch/EasyTimer.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; - -namespace Wox.Plugin.WebSearch -{ - public static class EasyTimer - { - public static IDisposable SetInterval(Action method, int delayInMilliseconds) - { - System.Timers.Timer timer = new System.Timers.Timer(delayInMilliseconds); - timer.Elapsed += (source, e) => - { - method(); - }; - - timer.Enabled = true; - timer.Start(); - - // Returns a stop handle which can be used for stopping - // the timer, if required - return timer as IDisposable; - } - - public static IDisposable SetTimeout(Action method, int delayInMilliseconds) - { - System.Timers.Timer timer = new System.Timers.Timer(delayInMilliseconds); - timer.Elapsed += (source, e) => - { - method(); - }; - - timer.AutoReset = false; - timer.Enabled = true; - timer.Start(); - - // Returns a stop handle which can be used for stopping - // the timer, if required - return timer as IDisposable; - } - } -} diff --git a/Plugins/Wox.Plugin.WebSearch/WebSearchPlugin.cs b/Plugins/Wox.Plugin.WebSearch/WebSearchPlugin.cs index d4ee840df0..f2dba4a35f 100644 --- a/Plugins/Wox.Plugin.WebSearch/WebSearchPlugin.cs +++ b/Plugins/Wox.Plugin.WebSearch/WebSearchPlugin.cs @@ -4,14 +4,14 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; +using System.Windows.Controls; using Wox.Plugin.WebSearch.SuggestionSources; namespace Wox.Plugin.WebSearch { public class WebSearchPlugin : IPlugin, ISettingProvider, IPluginI18n, IInstantQuery { - private PluginInitContext context; - private IDisposable suggestionTimer; + private PluginInitContext _context; public List Query(Query query) { @@ -21,71 +21,63 @@ namespace Wox.Plugin.WebSearch if (webSearch != null) { - string keyword = query.ActionKeyword; + string keyword = query.Search; string title = keyword; - string subtitle = context.API.GetTranslation("wox_plugin_websearch_search") + " " + webSearch.Title; + string subtitle = _context.API.GetTranslation("wox_plugin_websearch_search") + " " + webSearch.Title; if (string.IsNullOrEmpty(keyword)) { title = subtitle; - subtitle = null; + subtitle = string.Empty; } - context.API.PushResults(query, context.CurrentPluginMetadata, new List() + var result = new Result { - new Result() + Title = title, + SubTitle = subtitle, + Score = 6, + IcoPath = webSearch.IconPath, + Action = c => { - Title = title, - SubTitle = subtitle, - Score = 6, - IcoPath = webSearch.IconPath, - Action = (c) => - { - Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(keyword))); - return true; - } + Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(keyword ?? string.Empty))); + return true; } - }); + }; + results.Add(result); if (WebSearchStorage.Instance.EnableWebSearchSuggestion && !string.IsNullOrEmpty(keyword)) { - if (suggestionTimer != null) - { - suggestionTimer.Dispose(); - } - suggestionTimer = EasyTimer.SetTimeout(() => { QuerySuggestions(keyword, query, subtitle, webSearch); }, 350); + // todo use Task.Wait when .net upgraded + results.AddRange(ResultsFromSuggestions(keyword, subtitle, webSearch)); } } - return results; } - private void QuerySuggestions(string keyword, Query query, string subtitle, WebSearch webSearch) + private IEnumerable ResultsFromSuggestions(string keyword, string subtitle, WebSearch webSearch) { - ISuggestionSource sugg = SuggestionSourceFactory.GetSuggestionSource(WebSearchStorage.Instance.WebSearchSuggestionSource, context); - if (sugg != null) + ISuggestionSource sugg = SuggestionSourceFactory.GetSuggestionSource(WebSearchStorage.Instance.WebSearchSuggestionSource, _context); + var suggestions = sugg?.GetSuggestions(keyword); + if (suggestions != null) { - var result = sugg.GetSuggestions(keyword); - if (result != null) + var resultsFromSuggestion = suggestions.Select(o => new Result { - context.API.PushResults(query, context.CurrentPluginMetadata, - result.Select(o => new Result() - { - Title = o, - SubTitle = subtitle, - Score = 5, - IcoPath = webSearch.IconPath, - Action = (c) => - { - Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(o))); - return true; - } - }).ToList()); - } + Title = o, + SubTitle = subtitle, + Score = 5, + IcoPath = webSearch.IconPath, + Action = c => + { + Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(o))); + return true; + } + }); + return resultsFromSuggestion; } + return new List(); } public void Init(PluginInitContext context) { - this.context = context; + _context = context; if (WebSearchStorage.Instance.WebSearches == null) WebSearchStorage.Instance.WebSearches = WebSearchStorage.Instance.LoadDefaultWebSearches(); @@ -93,9 +85,9 @@ namespace Wox.Plugin.WebSearch #region ISettingProvider Members - public System.Windows.Controls.Control CreateSettingPanel() + public Control CreateSettingPanel() { - return new WebSearchesSetting(context); + return new WebSearchesSetting(_context); } #endregion @@ -107,12 +99,12 @@ namespace Wox.Plugin.WebSearch public string GetTranslatedPluginTitle() { - return context.API.GetTranslation("wox_plugin_websearch_plugin_name"); + return _context.API.GetTranslation("wox_plugin_websearch_plugin_name"); } public string GetTranslatedPluginDescription() { - return context.API.GetTranslation("wox_plugin_websearch_plugin_description"); + return _context.API.GetTranslation("wox_plugin_websearch_plugin_description"); } public bool IsInstantQuery(string query) => false; diff --git a/Plugins/Wox.Plugin.WebSearch/Wox.Plugin.WebSearch.csproj b/Plugins/Wox.Plugin.WebSearch/Wox.Plugin.WebSearch.csproj index 32a1b26e4a..a407ad508e 100644 --- a/Plugins/Wox.Plugin.WebSearch/Wox.Plugin.WebSearch.csproj +++ b/Plugins/Wox.Plugin.WebSearch/Wox.Plugin.WebSearch.csproj @@ -49,7 +49,6 @@ - @@ -93,9 +92,6 @@ Designer - - - {4fd29318-a8ab-4d8f-aa47-60bc241b8da3} diff --git a/Wox.Core/Plugin/CSharpPluginLoader.cs b/Wox.Core/Plugin/CSharpPluginLoader.cs index 7041d26c21..c5d1c78edc 100644 --- a/Wox.Core/Plugin/CSharpPluginLoader.cs +++ b/Wox.Core/Plugin/CSharpPluginLoader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using Wox.Core.Exception; using Wox.Infrastructure.Logger; using Wox.Plugin; @@ -19,10 +20,10 @@ namespace Wox.Core.Plugin try { Assembly asm = Assembly.Load(AssemblyName.GetAssemblyName(metadata.ExecuteFilePath)); - List types = asm.GetTypes().Where(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))).ToList(); + List types = asm.GetTypes().Where(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))).ToList(); if (types.Count == 0) { - Log.Warn(string.Format("Couldn't load plugin {0}: didn't find the class that implement IPlugin", metadata.Name)); + Log.Warn($"Couldn't load plugin {metadata.Name}: didn't find the class that implement IPlugin"); continue; } @@ -39,12 +40,7 @@ namespace Wox.Core.Plugin } catch (System.Exception e) { - Log.Error(string.Format("Couldn't load plugin {0}: {1}", metadata.Name, e.Message)); -#if (DEBUG) - { - throw; - } -#endif + Log.Error(new WoxPluginException(metadata.Name, $"Couldn't load plugin", e)); } } diff --git a/Wox.Core/Plugin/JsonRPCPlugin.cs b/Wox.Core/Plugin/JsonRPCPlugin.cs index 6f65d78c93..a7d5db8cc5 100644 --- a/Wox.Core/Plugin/JsonRPCPlugin.cs +++ b/Wox.Core/Plugin/JsonRPCPlugin.cs @@ -74,7 +74,7 @@ namespace Wox.Core.Plugin } catch (System.Exception e) { - Log.Error(e.Message); + Log.Error(e); } } return null; diff --git a/Wox.Core/Plugin/PluginConfig.cs b/Wox.Core/Plugin/PluginConfig.cs index 63abe41ff4..05f72bbe64 100644 --- a/Wox.Core/Plugin/PluginConfig.cs +++ b/Wox.Core/Plugin/PluginConfig.cs @@ -47,7 +47,7 @@ namespace Wox.Core.Plugin } catch (System.Exception e) { - Log.Error(ExceptionFormatter.FormatExcpetion(e)); + Log.Fatal(e); } } PluginMetadata metadata = GetPluginMetadata(directory); @@ -63,7 +63,7 @@ namespace Wox.Core.Plugin string configPath = Path.Combine(pluginDirectory, pluginConfigName); if (!File.Exists(configPath)) { - Log.Warn(string.Format("parse plugin {0} failed: didn't find config file.", configPath)); + Log.Warn($"parse plugin {configPath} failed: didn't find config file."); return null; } @@ -77,40 +77,25 @@ namespace Wox.Core.Plugin // for plugin still use old ActionKeyword metadata.ActionKeyword = metadata.ActionKeywords?[0]; } - catch (System.Exception) + catch (System.Exception e) { - string error = string.Format("Parse plugin config {0} failed: json format is not valid", configPath); - Log.Warn(error); -#if (DEBUG) - { - throw new WoxException(error); - } -#endif + string msg = $"Parse plugin config {configPath} failed: json format is not valid"; + Log.Error(new WoxException(msg)); return null; } if (!AllowedLanguage.IsAllowed(metadata.Language)) { - string error = string.Format("Parse plugin config {0} failed: invalid language {1}", configPath, metadata.Language); - Log.Warn(error); -#if (DEBUG) - { - throw new WoxException(error); - } -#endif + string msg = $"Parse plugin config {configPath} failed: invalid language {metadata.Language}"; + Log.Error(new WoxException(msg)); return null; } if (!File.Exists(metadata.ExecuteFilePath)) { - string error = string.Format("Parse plugin config {0} failed: ExecuteFile {1} didn't exist", configPath, metadata.ExecuteFilePath); - Log.Warn(error); -#if (DEBUG) - { - throw new WoxException(error); - } -#endif + string msg = $"Parse plugin config {configPath} failed: ExecuteFile {metadata.ExecuteFilePath} didn't exist"; + Log.Error(new WoxException(msg)); return null; } diff --git a/Wox.Core/Plugin/PluginManager.cs b/Wox.Core/Plugin/PluginManager.cs index 6123508c3a..06f10f7278 100644 --- a/Wox.Core/Plugin/PluginManager.cs +++ b/Wox.Core/Plugin/PluginManager.cs @@ -29,8 +29,8 @@ namespace Wox.Core.Plugin public static IEnumerable AllPlugins { get; private set; } - public static IEnumerable GlobalPlugins { get; private set; } - public static IEnumerable NonGlobalPlugins { get; private set; } + public static List GlobalPlugins { get; } = new List(); + public static Dictionary NonGlobalPlugins { get; } = new Dictionary(); private static IEnumerable InstantQueryPlugins { get; set; } public static IPublicAPI API { private set; get; } @@ -58,7 +58,7 @@ namespace Wox.Core.Plugin } catch (System.Exception e) { - Log.Error(e.Message); + Log.Error(e); } } } @@ -69,7 +69,7 @@ namespace Wox.Core.Plugin /// public static void Init(IPublicAPI api) { - if (api == null) throw new WoxCritialException("api is null"); + if (api == null) throw new WoxFatalException("api is null"); SetupPluginDirectories(); API = api; @@ -104,8 +104,20 @@ namespace Wox.Core.Plugin { InstantQueryPlugins = GetPluginsForInterface(); contextMenuPlugins = GetPluginsForInterface(); - GlobalPlugins = AllPlugins.Where(p => IsGlobalPlugin(p.Metadata)); - NonGlobalPlugins = AllPlugins.Where(p => !IsGlobalPlugin(p.Metadata)); + foreach (var plugin in AllPlugins) + { + if (IsGlobalPlugin(plugin.Metadata)) + { + GlobalPlugins.Add(plugin); + } + else + { + foreach (string actionKeyword in plugin.Metadata.ActionKeywords) + { + NonGlobalPlugins[actionKeyword] = plugin; + } + } + } }); } @@ -123,7 +135,7 @@ namespace Wox.Core.Plugin var search = rawQuery; List actionParameters = terms.ToList(); if (terms.Length == 0) return null; - if (IsVailldActionKeyword(terms[0])) + if (NonGlobalPlugins.ContainsKey(terms[0])) { actionKeyword = terms[0]; actionParameters = terms.Skip(1).ToList(); @@ -143,8 +155,8 @@ namespace Wox.Core.Plugin public static void QueryForAllPlugins(Query query) { - var pluginPairs = GetPluginForActionKeyword(query.ActionKeyword) != null ? - new List { GetPluginForActionKeyword(query.ActionKeyword) } : GlobalPlugins; + var pluginPairs = NonGlobalPlugins.ContainsKey(query.ActionKeyword) ? + new List { NonGlobalPlugins[query.ActionKeyword] } : GlobalPlugins; foreach (var plugin in pluginPairs) { var customizedPluginConfig = UserSettingStorage.Instance. @@ -152,7 +164,7 @@ namespace Wox.Core.Plugin if (customizedPluginConfig != null && customizedPluginConfig.Disabled) continue; if (IsInstantQueryPlugin(plugin)) { - Stopwatch.Debug($"Instant Query for {plugin.Metadata.Name}", () => + Stopwatch.Normal($"Instant QueryForPlugin for {plugin.Metadata.Name}", () => { QueryForPlugin(plugin, query); }); @@ -161,7 +173,10 @@ namespace Wox.Core.Plugin { ThreadPool.QueueUserWorkItem(state => { - QueryForPlugin(plugin, query); + Stopwatch.Normal($"Normal QueryForPlugin for {plugin.Metadata.Name}", () => + { + QueryForPlugin(plugin, query); + }); }); } } @@ -172,7 +187,7 @@ namespace Wox.Core.Plugin try { List results = new List(); - var milliseconds = Stopwatch.Normal($"Query for {pair.Metadata.Name}", () => + var milliseconds = Stopwatch.Normal($"Plugin.Query cost for {pair.Metadata.Name}", () => { results = pair.Plugin.Query(query) ?? results; results.ForEach(o => { o.PluginID = pair.Metadata.ID; }); @@ -183,25 +198,10 @@ namespace Wox.Core.Plugin } catch (System.Exception e) { - throw new WoxPluginException(pair.Metadata.Name, e); + throw new WoxPluginException(pair.Metadata.Name, $"QueryForPlugin failed", e); } } - /// - /// Check if a query contains valid action keyword - /// - /// - /// - private static bool IsVailldActionKeyword(string actionKeyword) - { - if (string.IsNullOrEmpty(actionKeyword) || actionKeyword == Query.GlobalPluginWildcardSign) return false; - PluginPair pair = AllPlugins.FirstOrDefault(o => o.Metadata.ActionKeywords.Contains(actionKeyword)); - if (pair == null) return false; - var customizedPluginConfig = UserSettingStorage.Instance. - CustomizedPluginConfigs.FirstOrDefault(o => o.ID == pair.Metadata.ID); - return customizedPluginConfig == null || !customizedPluginConfig.Disabled; - } - private static bool IsGlobalPlugin(PluginMetadata metadata) { return metadata.ActionKeywords.Contains(Query.GlobalPluginWildcardSign); @@ -225,13 +225,6 @@ namespace Wox.Core.Plugin return AllPlugins.FirstOrDefault(o => o.Metadata.ID == id); } - private static PluginPair GetPluginForActionKeyword(string actionKeyword) - { - //if a query doesn't contain a vaild action keyword, it should be a query for system plugin - if (string.IsNullOrEmpty(actionKeyword) || actionKeyword == Query.GlobalPluginWildcardSign) return null; - return NonGlobalPlugins.FirstOrDefault(o => o.Metadata.ActionKeywords.Contains(actionKeyword)); - } - public static IEnumerable GetPluginsForInterface() where T : IFeatures { return AllPlugins.Where(p => p.Plugin is T); @@ -249,12 +242,7 @@ namespace Wox.Core.Plugin } catch (System.Exception e) { - Log.Error($"Couldn't load plugin context menus {pluginPair.Metadata.Name}: {e.Message}"); -#if (DEBUG) - { - throw; - } -#endif + Log.Error(new WoxPluginException(pluginPair.Metadata.Name, $"Couldn't load plugin context menus", e)); } } diff --git a/Wox.Core/Theme/Theme.cs b/Wox.Core/Theme/Theme.cs index 114b5742c7..9d937b51b1 100644 --- a/Wox.Core/Theme/Theme.cs +++ b/Wox.Core/Theme/Theme.cs @@ -35,7 +35,7 @@ namespace Wox.Core.Theme } catch (System.Exception e) { - Log.Error(e.Message); + Log.Error(e); } } } diff --git a/Wox.Core/Wox.Core.csproj b/Wox.Core/Wox.Core.csproj index c7e8072a24..d69ae6a8d6 100644 --- a/Wox.Core/Wox.Core.csproj +++ b/Wox.Core/Wox.Core.csproj @@ -57,13 +57,6 @@ - - - - - - - diff --git a/Wox.Core/i18n/Internationalization.cs b/Wox.Core/i18n/Internationalization.cs index cc0d80e837..d94044a821 100644 --- a/Wox.Core/i18n/Internationalization.cs +++ b/Wox.Core/i18n/Internationalization.cs @@ -32,7 +32,7 @@ namespace Wox.Core.i18n } catch (System.Exception e) { - Log.Error(e.Message); + Log.Error(e); } } } @@ -122,12 +122,8 @@ namespace Wox.Core.i18n } catch (System.Exception e) { - Log.Warn("Update Plugin metadata translation failed:" + e.Message); -#if (DEBUG) - { - throw; - } -#endif + var woxPluginException = new WoxPluginException(pluginPair.Metadata.Name, "Update Plugin metadata translation failed:", e); + Log.Error(woxPluginException); } } diff --git a/Wox.Core/Exception/ExceptionFormatter.cs b/Wox.Infrastructure/Exception/ExceptionFormatter.cs similarity index 100% rename from Wox.Core/Exception/ExceptionFormatter.cs rename to Wox.Infrastructure/Exception/ExceptionFormatter.cs diff --git a/Wox.Core/Exception/WoxException.cs b/Wox.Infrastructure/Exception/WoxException.cs similarity index 99% rename from Wox.Core/Exception/WoxException.cs rename to Wox.Infrastructure/Exception/WoxException.cs index 2840e34aaa..bd23429ab6 100644 --- a/Wox.Core/Exception/WoxException.cs +++ b/Wox.Infrastructure/Exception/WoxException.cs @@ -14,7 +14,6 @@ public WoxException(string msg, System.Exception innerException) : base(msg, innerException) { - } } } diff --git a/Wox.Core/Exception/WoxCritialException.cs b/Wox.Infrastructure/Exception/WoxFatalException.cs similarity index 62% rename from Wox.Core/Exception/WoxCritialException.cs rename to Wox.Infrastructure/Exception/WoxFatalException.cs index cf840f9b19..21984f18d4 100644 --- a/Wox.Core/Exception/WoxCritialException.cs +++ b/Wox.Infrastructure/Exception/WoxFatalException.cs @@ -3,9 +3,9 @@ /// /// Represent exceptions that wox can't handle and MUST close running Wox. /// - public class WoxCritialException : WoxException + public class WoxFatalException : WoxException { - public WoxCritialException(string msg) : base(msg) + public WoxFatalException(string msg) : base(msg) { } } diff --git a/Wox.Core/Exception/WoxHttpException.cs b/Wox.Infrastructure/Exception/WoxHttpException.cs similarity index 100% rename from Wox.Core/Exception/WoxHttpException.cs rename to Wox.Infrastructure/Exception/WoxHttpException.cs diff --git a/Wox.Core/Exception/WoxI18nException.cs b/Wox.Infrastructure/Exception/WoxI18nException.cs similarity index 100% rename from Wox.Core/Exception/WoxI18nException.cs rename to Wox.Infrastructure/Exception/WoxI18nException.cs diff --git a/Wox.Core/Exception/WoxJsonRPCException.cs b/Wox.Infrastructure/Exception/WoxJsonRPCException.cs similarity index 100% rename from Wox.Core/Exception/WoxJsonRPCException.cs rename to Wox.Infrastructure/Exception/WoxJsonRPCException.cs diff --git a/Wox.Core/Exception/WoxPluginException.cs b/Wox.Infrastructure/Exception/WoxPluginException.cs similarity index 60% rename from Wox.Core/Exception/WoxPluginException.cs rename to Wox.Infrastructure/Exception/WoxPluginException.cs index e435be5cdd..bc1854f564 100644 --- a/Wox.Core/Exception/WoxPluginException.cs +++ b/Wox.Infrastructure/Exception/WoxPluginException.cs @@ -4,8 +4,8 @@ { public string PluginName { get; set; } - public WoxPluginException(string pluginName,System.Exception e) - : base(e.Message,e) + public WoxPluginException(string pluginName, string msg, System.Exception e) + : base($"{msg}: {pluginName}", e) { PluginName = pluginName; } diff --git a/Wox.Infrastructure/Logger/Log.cs b/Wox.Infrastructure/Logger/Log.cs index 8abaf16a4f..e03c7045bc 100644 --- a/Wox.Infrastructure/Logger/Log.cs +++ b/Wox.Infrastructure/Logger/Log.cs @@ -1,5 +1,6 @@ using System; using NLog; +using Wox.Core.Exception; namespace Wox.Infrastructure.Logger { @@ -7,34 +8,40 @@ namespace Wox.Infrastructure.Logger { private static NLog.Logger logger = LogManager.GetCurrentClassLogger(); - public static void Error(string msg) - { - logger.Error(msg); - } - public static void Error(Exception e) { +#if DEBUG + throw e; +#else logger.Error(e.Message + "\r\n" + e.StackTrace); +#endif } public static void Debug(string msg) { + System.Diagnostics.Debug.WriteLine($"DEBUG: {msg}"); logger.Debug(msg); } public static void Info(string msg) { + System.Diagnostics.Debug.WriteLine($"INFO: {msg}"); logger.Info(msg); } public static void Warn(string msg) { + System.Diagnostics.Debug.WriteLine($"WARN: {msg}"); logger.Warn(msg); } - public static void Fatal(string msg) + public static void Fatal(Exception e) { - logger.Fatal(msg); +#if DEBUG + throw e; +#else + logger.Fatal(ExceptionFormatter.FormatExcpetion(e)); +#endif } } } diff --git a/Wox.Infrastructure/Stopwatch.cs b/Wox.Infrastructure/Stopwatch.cs index c0d8288540..7387734d5b 100644 --- a/Wox.Infrastructure/Stopwatch.cs +++ b/Wox.Infrastructure/Stopwatch.cs @@ -18,14 +18,6 @@ namespace Wox.Infrastructure #endif } - [Conditional("DEBUG")] - private static void WriteTimeInfo(string name, long milliseconds) - { - string info = $"{name} : {milliseconds}ms"; - System.Diagnostics.Debug.WriteLine(info); - Log.Info(info); - } - /// /// This stopwatch will also appear only in Debug mode /// @@ -36,7 +28,8 @@ namespace Wox.Infrastructure action(); stopWatch.Stop(); var milliseconds = stopWatch.ElapsedMilliseconds; - WriteTimeInfo(name, milliseconds); + string info = $"{name} : {milliseconds}ms"; + Log.Debug(info); return milliseconds; } diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index e3740ee2cc..31100d3ae4 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -46,9 +46,17 @@ + + + + + + + + diff --git a/Wox.Test/Plugins/PluginInitTest.cs b/Wox.Test/Plugins/PluginInitTest.cs index 3bb5658c5e..e23fa54322 100644 --- a/Wox.Test/Plugins/PluginInitTest.cs +++ b/Wox.Test/Plugins/PluginInitTest.cs @@ -11,7 +11,7 @@ namespace Wox.Test.Plugins [Test] public void PublicAPIIsNullTest() { - Assert.Throws(typeof(WoxCritialException), () => PluginManager.Init(null)); + Assert.Throws(typeof(WoxFatalException), () => PluginManager.Init(null)); } } } diff --git a/Wox.sln b/Wox.sln index d50b7e3b37..efad41543f 100644 --- a/Wox.sln +++ b/Wox.sln @@ -62,6 +62,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.Everything", "Plugins\Wox.Plugin.Everything\Wox.Plugin.Everything.csproj", "{230AE83F-E92E-4E69-8355-426B305DA9C0}" EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU diff --git a/Wox/Helper/ErrorReporting.cs b/Wox/Helper/ErrorReporting.cs index e7b5a81fb9..63c6f80dc8 100644 --- a/Wox/Helper/ErrorReporting.cs +++ b/Wox/Helper/ErrorReporting.cs @@ -9,7 +9,7 @@ namespace Wox.Helper { public static void Report(Exception e) { - Log.Error(ExceptionFormatter.FormatExcpetion(e)); + Log.Fatal(e); new CrashReporter.CrashReporter(e).Show(); } diff --git a/Wox/Helper/ListBoxItems.cs b/Wox/Helper/ListBoxItems.cs new file mode 100644 index 0000000000..5d62edf11d --- /dev/null +++ b/Wox/Helper/ListBoxItems.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using Wox.Plugin; + +namespace Wox.Helper +{ + class ListBoxItems : ObservableCollection + // todo implement custom moveItem,removeItem,insertItem for better performance + { + public void RemoveAll(Predicate predicate) + { + CheckReentrancy(); + + List itemsToRemove = Items.Where(x => predicate(x)).ToList(); + if (itemsToRemove.Count > 0) + { + + itemsToRemove.ForEach(item => Items.Remove(item)); + + OnPropertyChanged(new PropertyChangedEventArgs("Count")); + OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + // fuck ms + // http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx + // http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx + // PS: don't use Reset for other data updates, it will cause UI flickering + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } + + public void Update(List newItems) + { + int newCount = newItems.Count; + int oldCount = Items.Count; + int location = newCount > oldCount ? oldCount : newCount; + for (int i = 0; i < location; i++) + { + Result oldItem = Items[i]; + Result newItem = newItems[i]; + if (!Equals(oldItem, newItem)) + { + this[i] = newItem; + } + } + + if (newCount > oldCount) + { + for (int i = oldCount; i < newCount; i++) + { + Add(newItems[i]); + } + } + else + { + int removeIndex = newCount; + for (int i = newCount; i < oldCount; i++) + { + RemoveAt(removeIndex); + } + } + + } + } +} diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index 13481cd019..5a289131ed 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -32,6 +32,7 @@ using IDataObject = System.Windows.IDataObject; using KeyEventArgs = System.Windows.Input.KeyEventArgs; using MenuItem = System.Windows.Forms.MenuItem; using MessageBox = System.Windows.MessageBox; +using Stopwatch = Wox.Infrastructure.Stopwatch; using ToolTip = System.Windows.Controls.ToolTip; namespace Wox @@ -43,11 +44,11 @@ namespace Wox private readonly Storyboard progressBarStoryboard = new Storyboard(); private NotifyIcon notifyIcon; - private bool queryHasReturn; - private string lastQuery; + private bool _queryHasReturn; + private Query _lastQuery = new Query(); private ToolTip toolTip = new ToolTip(); - private bool ignoreTextChange = false; + private bool _ignoreTextChange = false; private List CurrentContextMenus = new List(); private string textBeforeEnterContextMenuMode; @@ -72,7 +73,7 @@ namespace Wox { Dispatcher.Invoke(new Action(() => { - ignoreTextChange = true; + _ignoreTextChange = true; tbQuery.Text = query; tbQuery.CaretIndex = tbQuery.Text.Length; if (selectAll) @@ -162,7 +163,7 @@ namespace Wox o.PluginID = plugin.ID; o.OriginQuery = query; }); - UpdateResultView(results); + UpdateResultView(results, plugin, query); } public void ShowContextMenu(PluginMetadata plugin, List results) @@ -176,7 +177,7 @@ namespace Wox o.ContextMenu = null; }); pnlContextMenu.Clear(); - pnlContextMenu.AddResults(results); + pnlContextMenu.AddResults(results, plugin.ID); pnlContextMenu.Visibility = Visibility.Visible; pnlResult.Visibility = Visibility.Collapsed; } @@ -418,11 +419,12 @@ namespace Wox private void QueryContextMenu() { + var contextMenuId = "Context Menu Id"; pnlContextMenu.Clear(); var query = tbQuery.Text.ToLower(); if (string.IsNullOrEmpty(query)) { - pnlContextMenu.AddResults(CurrentContextMenus); + pnlContextMenu.AddResults(CurrentContextMenus, contextMenuId); } else { @@ -435,32 +437,25 @@ namespace Wox filterResults.Add(contextMenu); } } - pnlContextMenu.AddResults(filterResults); + pnlContextMenu.AddResults(filterResults, contextMenuId); } } private void TbQuery_OnTextChanged(object sender, TextChangedEventArgs e) { - - if (ignoreTextChange) { ignoreTextChange = false; return; } + if (_ignoreTextChange) { _ignoreTextChange = false; return; } - if (!string.IsNullOrEmpty(tbQuery.Text.Trim())) + toolTip.IsOpen = false; + if (IsInContextMenuMode) { - toolTip.IsOpen = false; - if (IsInContextMenuMode) - { - QueryContextMenu(); - return; - } + QueryContextMenu(); + return; + } - Query(tbQuery.Text); - Dispatcher.DelayInvoke("ShowProgressbar", () => - { - if (!string.IsNullOrEmpty(tbQuery.Text.Trim()) && tbQuery.Text != lastQuery && !queryHasReturn) - { - StartProgress(); - } - }, TimeSpan.FromMilliseconds(150)); + string query = tbQuery.Text.Trim(); + if (!string.IsNullOrEmpty(query)) + { + Query(query); //reset query history index after user start new query ResetQueryHistoryIndex(); } @@ -472,14 +467,44 @@ namespace Wox private void ResetQueryHistoryIndex() { + pnlResult.RemoveResultsFor(QueryHistoryStorage.MetaData); QueryHistoryStorage.Instance.Reset(); } private void Query(string text) { + _queryHasReturn = false; var query = PluginManager.QueryInit(text); if (query != null) { - lastQuery = query.RawQuery; + // handle the exclusiveness of plugin using action keyword + string lastKeyword = _lastQuery.ActionKeyword; + string keyword = query.ActionKeyword; + if (string.IsNullOrEmpty(lastKeyword)) + { + if (!string.IsNullOrEmpty(keyword)) + { + pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); + } + } + else + { + if (string.IsNullOrEmpty(keyword)) + { + pnlResult.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata); + } + else if (lastKeyword != keyword) + { + pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata); + } + } + _lastQuery = query; + Dispatcher.DelayInvoke("ShowProgressbar", () => + { + if (!string.IsNullOrEmpty(query.RawQuery) && query.RawQuery == _lastQuery.RawQuery && !_queryHasReturn) + { + StartProgress(); + } + }, TimeSpan.FromMilliseconds(150)); PluginManager.QueryForAllPlugins(query); } StopProgress(); @@ -716,9 +741,11 @@ namespace Wox { if (history != null) { + var historyMetadata = QueryHistoryStorage.MetaData; ChangeQueryText(history.Query, true); var executeQueryHistoryTitle = GetTranslation("executeQuery"); var lastExecuteTime = GetTranslation("lastExecuteTime"); + pnlResult.RemoveResultsExcept(historyMetadata); UpdateResultViewInternal(new List() { new Result(){ @@ -731,7 +758,7 @@ namespace Wox return false; } } - }); + }, historyMetadata); } } @@ -812,28 +839,27 @@ namespace Wox } } - private void UpdateResultView(List list) + private void UpdateResultView(List list, PluginMetadata metadata, Query originQuery) { - queryHasReturn = true; + _queryHasReturn = true; progressBar.Dispatcher.Invoke(new Action(StopProgress)); - if (list == null || list.Count == 0) return; - if (list.Count > 0) + list.ForEach(o => { - list.ForEach(o => - { - o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5; - }); - List l = list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == lastQuery).ToList(); - UpdateResultViewInternal(l); + o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5; + }); + if (originQuery.RawQuery == _lastQuery.RawQuery) + { + UpdateResultViewInternal(list, metadata); } } - private void UpdateResultViewInternal(List list) + private void UpdateResultViewInternal(List list, PluginMetadata metadata) { Dispatcher.Invoke(new Action(() => { - pnlResult.AddResults(list); + Stopwatch.Normal($"UI update cost for {metadata.Name}", + () => { pnlResult.AddResults(list, metadata.ID); }); })); } @@ -882,7 +908,7 @@ namespace Wox textBeforeEnterContextMenuMode = tbQuery.Text; ChangeQueryText(""); pnlContextMenu.Clear(); - pnlContextMenu.AddResults(results); + pnlContextMenu.AddResults(results, result.PluginID); CurrentContextMenus = results; pnlContextMenu.Visibility = Visibility.Visible; pnlResult.Visibility = Visibility.Collapsed; diff --git a/Wox/Properties/Resources.Designer.cs b/Wox/Properties/Resources.Designer.cs index 7665c71436..20e0fb2257 100644 --- a/Wox/Properties/Resources.Designer.cs +++ b/Wox/Properties/Resources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// 此代码由工具生成。 -// 运行时版本:4.0.30319.0 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace Wox.Properties { /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ namespace Wox.Properties { } /// - /// 返回此类使用的缓存的 ResourceManager 实例。 + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ namespace Wox.Properties { } /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ namespace Wox.Properties { } /// - /// 查找类似于 (Icon) 的 System.Drawing.Icon 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// internal static System.Drawing.Icon app { get { diff --git a/Wox/Properties/Settings.Designer.cs b/Wox/Properties/Settings.Designer.cs index eb3d4dc309..7a4226349e 100644 --- a/Wox/Properties/Settings.Designer.cs +++ b/Wox/Properties/Settings.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// 此代码由工具生成。 -// 运行时版本:4.0.30319.0 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -12,7 +12,7 @@ namespace Wox.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/Wox/ResultPanel.xaml.cs b/Wox/ResultPanel.xaml.cs index 3115ece6ef..121ee9b852 100644 --- a/Wox/ResultPanel.xaml.cs +++ b/Wox/ResultPanel.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -9,6 +10,7 @@ using System.Windows.Media; using System.Linq; using System.Runtime.Remoting.Contexts; using Wox.Core.UserSettings; +using Wox.Helper; using Wox.Plugin; using Wox.Storage; @@ -20,7 +22,7 @@ namespace Wox public event Action LeftMouseClickEvent; public event Action RightMouseClickEvent; public event Action ItemDropEvent; - private readonly ObservableCollection _results; //todo, for better performance, override the default linear search + private readonly ListBoxItems _results; private readonly object _resultsUpdateLock = new object(); protected virtual void OnRightMouseClick(Result result) @@ -38,65 +40,74 @@ namespace Wox public int MaxResultsToShow { get { return UserSettingStorage.Instance.MaxResultsToShow * 50; } } - public void AddResults(List newResults) + internal void RemoveResultsFor(PluginMetadata metadata) { - if (newResults != null && newResults.Count > 0) + lock (_resultsUpdateLock) { - lock (_resultsUpdateLock) + _results.RemoveAll(r => r.PluginID == metadata.ID); + } + } + + internal void RemoveResultsExcept(PluginMetadata metadata) + { + lock (_resultsUpdateLock) + { + _results.RemoveAll(r => r.PluginID != metadata.ID); + } + } + + public void AddResults(List newResults, string resultId) + { + lock (_resultsUpdateLock) + { + var resultCopy = _results.ToList(); + var oldResults = resultCopy.Where(r => r.PluginID == resultId).ToList(); + // intersection of A (old results) and B (new newResults) + var intersection = oldResults.Intersect(newResults).ToList(); + // remove result of relative complement of B in A + foreach (var result in oldResults.Except(intersection)) { - var pluginId = newResults[0].PluginID; - var actionKeyword = newResults[0].OriginQuery.ActionKeyword; - List oldResults; - if (string.IsNullOrEmpty(actionKeyword)) - { - oldResults = _results.Where(r => r.PluginID == pluginId).ToList(); - } - else - { - oldResults = _results.ToList(); - } - // intersection of A (old results) and B (new newResults) - var intersection = oldResults.Intersect(newResults).ToList(); - - // remove result of relative complement of B in A - foreach (var result in oldResults.Except(intersection)) - { - _results.Remove(result); - } - - // update scores - foreach (var result in newResults) - { - if (IsTopMostResult(result)) - { - result.Score = int.MaxValue; - } - } - - // update index for result in intersection of A and B - foreach (var result in intersection) - { - int oldIndex = _results.IndexOf(result); - int oldScore = _results[oldIndex].Score; - if (result.Score != oldScore) - { - int newIndex = InsertIndexOf(result.Score); - if (newIndex != oldIndex) - { - _results.Move(oldIndex, newIndex); - } - } - } - - // insert result in relative complement of A in B - foreach (var result in newResults.Except(intersection)) - { - int newIndex = InsertIndexOf(result.Score); - _results.Insert(newIndex, result); - } - lbResults.Margin = lbResults.Items.Count > 0 ? new Thickness { Top = 8 } : new Thickness { Top = 0 }; - SelectFirst(); + resultCopy.Remove(result); } + + // update scores + foreach (var result in newResults) + { + if (IsTopMostResult(result)) + { + result.Score = int.MaxValue; + } + } + + // update index for result in intersection of A and B + foreach (var result in intersection) + { + int oldIndex = resultCopy.IndexOf(result); + int oldScore = resultCopy[oldIndex].Score; + if (result.Score != oldScore) + { + int newIndex = InsertIndexOf(result.Score, resultCopy); + if (newIndex != oldIndex) + { + var item = resultCopy[oldIndex]; + resultCopy.RemoveAt(oldIndex); + resultCopy.Insert(newIndex, item); + } + } + } + + // insert result in relative complement of A in B + foreach (var result in newResults.Except(intersection)) + { + int newIndex = InsertIndexOf(result.Score, resultCopy); + resultCopy.Insert(newIndex, result); + } + + // update UI in one run, so it can avoid UI flickering + _results.Update(resultCopy); + + lbResults.Margin = lbResults.Items.Count > 0 ? new Thickness { Top = 8 } : new Thickness { Top = 0 }; + SelectFirst(); } } @@ -105,13 +116,13 @@ namespace Wox return TopMostRecordStorage.Instance.IsTopMost(result); } - private int InsertIndexOf(int newScore) + private int InsertIndexOf(int newScore, IList list) { int index = 0; - for (; index < lbResults.Items.Count; index++) + for (; index < list.Count; index++) { - Result result = lbResults.Items[index] as Result; - if (newScore > result?.Score) + var result = list[index]; + if (newScore > result.Score) { break; } @@ -236,7 +247,7 @@ namespace Wox public ResultPanel() { InitializeComponent(); - _results = new ObservableCollection(); + _results = new ListBoxItems(); lbResults.ItemsSource = _results; } diff --git a/Wox/SettingWindow.xaml.cs b/Wox/SettingWindow.xaml.cs index 09e269d56c..5b57bb8d85 100644 --- a/Wox/SettingWindow.xaml.cs +++ b/Wox/SettingWindow.xaml.cs @@ -429,7 +429,7 @@ namespace Wox IcoPath = "Images/app.png", PluginDirectory = Path.GetDirectoryName(Application.ExecutablePath) } - }); + }, "test id"); foreach (string theme in ThemeManager.Theme.LoadAvailableThemes()) { diff --git a/Wox/Storage/QueryHistoryStorage.cs b/Wox/Storage/QueryHistoryStorage.cs index d39ebaad14..81ccc353ae 100644 --- a/Wox/Storage/QueryHistoryStorage.cs +++ b/Wox/Storage/QueryHistoryStorage.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using Newtonsoft.Json; using Wox.Infrastructure.Storage; +using Wox.Plugin; namespace Wox.Storage { @@ -16,6 +17,9 @@ namespace Wox.Storage private int MaxHistory = 300; private int cursor = 0; + public static PluginMetadata MetaData { get; } = new PluginMetadata + { ID = "Query history", Name = "Query history" }; + protected override string ConfigFolder { get { return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Config"); } @@ -77,7 +81,7 @@ namespace Wox.Storage return History.OrderByDescending(o => o.ExecutedDateTime).ToList(); } } - + public class HistoryItem { public string Query { get; set; } diff --git a/Wox/Wox.csproj b/Wox/Wox.csproj index ff6ac6882c..7d46e6109a 100644 --- a/Wox/Wox.csproj +++ b/Wox/Wox.csproj @@ -106,6 +106,7 @@ + @@ -280,7 +281,9 @@ ResXFileCodeGenerator Resources.Designer.cs - + + Designer + SettingsSingleFileGenerator Settings.Designer.cs @@ -305,9 +308,6 @@ Wox.Plugin - - -