From c5d7427cdfa823b23c0212b0f9fe3d3d5212a22e Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 11:36:21 +0800 Subject: [PATCH 01/11] everything query refactoring 1 more cacelation 2 remove lock 3 remove unnecessory ipc call --- .../Everything/EverythingAPI.cs | 125 ++++++++---------- Plugins/Wox.Plugin.Everything/Main.cs | 24 ++-- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs b/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs index a783f97df..b8ce31dab 100644 --- a/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs +++ b/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs @@ -13,8 +13,6 @@ namespace Wox.Plugin.Everything.Everything public sealed class EverythingApi { - private readonly object _syncObject = new object(); - public enum StateCode { OK, @@ -111,17 +109,6 @@ public bool EnableRegex } } - /// - /// Resets this instance. - /// - public void Reset() - { - lock (_syncObject) - { - EverythingApiDllImport.Everything_Reset(); - } - } - /// /// Searches the specified key word and reset the everything API afterwards /// @@ -132,77 +119,79 @@ public void Reset() /// public List Search(string keyWord, CancellationToken token, int maxCount) { + var results = new List(); + if (string.IsNullOrEmpty(keyWord)) throw new ArgumentNullException(nameof(keyWord)); if (maxCount < 0) throw new ArgumentOutOfRangeException(nameof(maxCount)); - lock (_syncObject) - { - if (keyWord.StartsWith("@")) - { - EverythingApiDllImport.Everything_SetRegex(true); - keyWord = keyWord.Substring(1); - } - else - { - EverythingApiDllImport.Everything_SetRegex(false); - } + if (token.IsCancellationRequested) { return results; } + if (keyWord.StartsWith("@")) + { + EverythingApiDllImport.Everything_SetRegex(true); + keyWord = keyWord.Substring(1); + } + else + { + EverythingApiDllImport.Everything_SetRegex(false); + } - EverythingApiDllImport.Everything_SetRequestFlags(RequestFlag.HighlightedFileName | RequestFlag.HighlightedFullPathAndFileName); - EverythingApiDllImport.Everything_SetOffset(0); - EverythingApiDllImport.Everything_SetMax(maxCount); - EverythingApiDllImport.Everything_SetSearchW(keyWord); + if (token.IsCancellationRequested) { return results; } + EverythingApiDllImport.Everything_SetRequestFlags(RequestFlag.HighlightedFileName | RequestFlag.HighlightedFullPathAndFileName); + if (token.IsCancellationRequested) { return results; } + EverythingApiDllImport.Everything_SetOffset(0); + if (token.IsCancellationRequested) { return results; } + EverythingApiDllImport.Everything_SetMax(maxCount); + if (token.IsCancellationRequested) { return results; } + EverythingApiDllImport.Everything_SetSearchW(keyWord); + + if (token.IsCancellationRequested) { return results; } + if (!EverythingApiDllImport.Everything_QueryW(true)) + { + CheckAndThrowExceptionOnError(); + return results; + } - if (token.IsCancellationRequested) + if (token.IsCancellationRequested) { return results; } + int count = EverythingApiDllImport.Everything_GetNumResults(); + for (int idx = 0; idx < count; ++idx) + { + if (token.IsCancellationRequested) { return results; } + // https://www.voidtools.com/forum/viewtopic.php?t=8169 + string fileNameHighted = Marshal.PtrToStringUni(EverythingApiDllImport.Everything_GetResultHighlightedFileNameW(idx)); + string fullPathHighted = Marshal.PtrToStringUni(EverythingApiDllImport.Everything_GetResultHighlightedFullPathAndFileNameW(idx)); + if (fileNameHighted == null | fullPathHighted == null) { - return null; + CheckAndThrowExceptionOnError(); } + if (token.IsCancellationRequested) { return results; } + ConvertHightlightFormat(fileNameHighted, out List fileNameHightlightData, out string fileName); + if (token.IsCancellationRequested) { return results; } + ConvertHightlightFormat(fullPathHighted, out List fullPathHightlightData, out string fullPath); - - if (!EverythingApiDllImport.Everything_QueryW(true)) + var result = new SearchResult { - CheckAndThrowExceptionOnError(); - return null; + FileName = fileName, + FileNameHightData = fileNameHightlightData, + FullPath = fullPath, + FullPathHightData = fullPathHightlightData, + }; + + if (token.IsCancellationRequested) { return results; } + if (EverythingApiDllImport.Everything_IsFolderResult(idx)) + { + result.Type = ResultType.Folder; } - - var results = new List(); - int count = EverythingApiDllImport.Everything_GetNumResults(); - for (int idx = 0; idx < count; ++idx) + else { - if (token.IsCancellationRequested) - { - return null; - } - // https://www.voidtools.com/forum/viewtopic.php?t=8169 - string fileNameHighted = Marshal.PtrToStringUni(EverythingApiDllImport.Everything_GetResultHighlightedFileNameW(idx)); - string fullPathHighted = Marshal.PtrToStringUni(EverythingApiDllImport.Everything_GetResultHighlightedFullPathAndFileNameW(idx)); - if (fileNameHighted == null | fullPathHighted == null) - { - CheckAndThrowExceptionOnError(); - } - ConvertHightlightFormat(fileNameHighted, out List fileNameHightlightData, out string fileName); - ConvertHightlightFormat(fullPathHighted, out List fullPathHightlightData, out string fullPath); - - var result = new SearchResult - { - FileName = fileName, - FileNameHightData = fileNameHightlightData, - FullPath = fullPath, - FullPathHightData = fullPathHightlightData, - }; - if (EverythingApiDllImport.Everything_IsFolderResult(idx)) - result.Type = ResultType.Folder; - else if (EverythingApiDllImport.Everything_IsFileResult(idx)) - result.Type = ResultType.File; - - results.Add(result); + result.Type = ResultType.File; } - Reset(); - - return results; + results.Add(result); } + + return results; } private static void ConvertHightlightFormat(string contentHightlighted, out List hightlightData, out string fn) diff --git a/Plugins/Wox.Plugin.Everything/Main.cs b/Plugins/Wox.Plugin.Everything/Main.cs index 24c7cefec..342e0669f 100644 --- a/Plugins/Wox.Plugin.Everything/Main.cs +++ b/Plugins/Wox.Plugin.Everything/Main.cs @@ -28,7 +28,7 @@ public class Main : IPlugin, ISettingProvider, IPluginI18n, IContextMenu, ISavab private Settings _settings; private PluginJsonStorage _storage; - private CancellationTokenSource _cancellationTokenSource; + private CancellationTokenSource _updateSource; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); public void Save() @@ -38,8 +38,16 @@ public void Save() public List Query(Query query) { - _cancellationTokenSource?.Cancel(); // cancel if already exist - var cts = _cancellationTokenSource = new CancellationTokenSource(); + if (_updateSource != null && !_updateSource.IsCancellationRequested) + { + _updateSource.Cancel(); + Logger.WoxDebug($"cancel init {_updateSource.Token.GetHashCode()} {Thread.CurrentThread.ManagedThreadId} {query.RawQuery}"); + _updateSource.Dispose(); + } + var source = new CancellationTokenSource(); + _updateSource = source; + var token = source.Token; + var results = new List(); if (!string.IsNullOrEmpty(query.Search)) { @@ -47,14 +55,12 @@ public List Query(Query query) try { - var searchList = _api.Search(keyword, cts.Token, _settings.MaxSearchCount); - if (searchList == null) - { - return results; - } - + if (token.IsCancellationRequested) { return results; } + var searchList = _api.Search(keyword, token, _settings.MaxSearchCount); + if (token.IsCancellationRequested) { return results; } for (int i = 0; i < searchList.Count; i++) { + if (token.IsCancellationRequested) { return results; } SearchResult searchResult = searchList[i]; var r = CreateResult(keyword, searchResult, i); results.Add(r); From 50ed1622ff1dcf95eb63359d64fd44d2d98a4905 Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 11:41:00 +0800 Subject: [PATCH 02/11] remove useless variable --- Plugins/Wox.Plugin.Program/Main.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Plugins/Wox.Plugin.Program/Main.cs b/Plugins/Wox.Plugin.Program/Main.cs index 0d855f421..ff70d7547 100644 --- a/Plugins/Wox.Plugin.Program/Main.cs +++ b/Plugins/Wox.Plugin.Program/Main.cs @@ -24,6 +24,7 @@ public class Main : ISettingProvider, IPlugin, IPluginI18n, IContextMenu, ISavab internal static Settings _settings { get; set; } private static PluginInitContext _context; + private CancellationTokenSource _updateSource; private static BinaryStorage _win32Storage; private static BinaryStorage _uwpStorage; @@ -53,16 +54,12 @@ public void Save() public List Query(Query query) { - Win32[] win32; - UWP.Application[] uwps; - win32 = _win32s; - uwps = _uwps; - var results1 = win32.AsParallel() + var results1 = _win32s.AsParallel() .Where(p => p.Enabled) .Select(p => p.Result(query.Search, _context.API)); - var results2 = uwps.AsParallel() + var results2 = _uwps.AsParallel() .Where(p => p.Enabled) .Select(p => p.Result(query.Search, _context.API)); From d8f4023d4eabf2da72f20b6e492b0a1b700d05d0 Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 11:45:49 +0800 Subject: [PATCH 03/11] basci cancelation for program plugin query --- Plugins/Wox.Plugin.Program/Main.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Plugins/Wox.Plugin.Program/Main.cs b/Plugins/Wox.Plugin.Program/Main.cs index ff70d7547..741888454 100644 --- a/Plugins/Wox.Plugin.Program/Main.cs +++ b/Plugins/Wox.Plugin.Program/Main.cs @@ -54,19 +54,32 @@ public void Save() public List Query(Query query) { + if (_updateSource != null && !_updateSource.IsCancellationRequested) + { + _updateSource.Cancel(); + Logger.WoxDebug($"cancel init {_updateSource.Token.GetHashCode()} {Thread.CurrentThread.ManagedThreadId} {query.RawQuery}"); + _updateSource.Dispose(); + } + var source = new CancellationTokenSource(); + _updateSource = source; + var token = source.Token; + if (token.IsCancellationRequested) { return new List(); } var results1 = _win32s.AsParallel() .Where(p => p.Enabled) .Select(p => p.Result(query.Search, _context.API)); - + + if (token.IsCancellationRequested) { return new List(); } var results2 = _uwps.AsParallel() .Where(p => p.Enabled) .Select(p => p.Result(query.Search, _context.API)); + if (token.IsCancellationRequested) { return new List(); } var result = results1.Concat(results2) .Where(r => r != null && r.Score > 0) .Where(p => !_settings.IgnoredSequence.Any(entry => { + if (token.IsCancellationRequested) { return false; } if (entry.IsRegex) { return Regex.Match(p.Title, entry.EntryString).Success; From e2a3ca486096f13f2e6364d441a7565b86c35088 Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 12:39:51 +0800 Subject: [PATCH 04/11] fine grained cancelation for program plugin and fix sort --- Plugins/Wox.Plugin.Program/Main.cs | 72 +++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/Plugins/Wox.Plugin.Program/Main.cs b/Plugins/Wox.Plugin.Program/Main.cs index 741888454..0134b3aa6 100644 --- a/Plugins/Wox.Plugin.Program/Main.cs +++ b/Plugins/Wox.Plugin.Program/Main.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -13,6 +14,7 @@ using Wox.Plugin.Program.Views; using Stopwatch = Wox.Infrastructure.Stopwatch; using System.Threading; +using Windows.ApplicationModel.Background; namespace Wox.Plugin.Program { @@ -64,34 +66,62 @@ public List Query(Query query) _updateSource = source; var token = source.Token; - if (token.IsCancellationRequested) { return new List(); } - var results1 = _win32s.AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)); - - if (token.IsCancellationRequested) { return new List(); } - var results2 = _uwps.AsParallel() - .Where(p => p.Enabled) - .Select(p => p.Result(query.Search, _context.API)); + ConcurrentBag resultRaw = new ConcurrentBag(); if (token.IsCancellationRequested) { return new List(); } - var result = results1.Concat(results2) - .Where(r => r != null && r.Score > 0) - .Where(p => !_settings.IgnoredSequence.Any(entry => + Parallel.ForEach(_win32s, (program, state) => { - if (token.IsCancellationRequested) { return false; } - if (entry.IsRegex) + if (token.IsCancellationRequested) { state.Break(); } + if (program.Enabled) { - return Regex.Match(p.Title, entry.EntryString).Success; + var r = program.Result(query.Search, _context.API); + if (r != null && r.Score > 0) + { + resultRaw.Add(r); + } } - else + }); + if (token.IsCancellationRequested) { return new List(); } + Parallel.ForEach(_uwps, (program, state) => + { + if (token.IsCancellationRequested) { state.Break(); } + if (program.Enabled) { - return p.Title.ToLower().Contains(entry.EntryString); + var r = program.Result(query.Search, _context.API); + if (token.IsCancellationRequested) { state.Break(); } + if (r != null && r.Score > 0) + { + resultRaw.Add(r); + } } - })).Take(30); - + }); - return result.ToList(); + if (token.IsCancellationRequested) { return new List(); } + OrderedParallelQuery sorted = resultRaw.AsParallel().OrderByDescending(r => r.Score); + List results = new List(); + foreach (Result r in sorted) { + if (token.IsCancellationRequested) { return new List(); } + var ignored = _settings.IgnoredSequence.Any(entry => + { + if (entry.IsRegex) + { + return Regex.Match(r.Title, entry.EntryString).Success; + } + else + { + return r.Title.ToLower().Contains(entry.EntryString); + } + }); + if (!ignored) + { + results.Add(r); + } + if (results.Count == 30) + { + break; + } + } + return results; } public void Init(PluginInitContext context) @@ -202,4 +232,4 @@ public void ReloadData() IndexPrograms(); } } -} \ No newline at end of file +} From 04985e1cbf2240b3828e84874c91f5dda9ec9d04 Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 12:49:03 +0800 Subject: [PATCH 05/11] fix everything hight data index --- Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs b/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs index b8ce31dab..ede391535 100644 --- a/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs +++ b/Plugins/Wox.Plugin.Everything/Everything/EverythingAPI.cs @@ -200,18 +200,20 @@ private static void ConvertHightlightFormat(string contentHightlighted, out List StringBuilder content = new StringBuilder(); bool flag = false; char[] contentArray = contentHightlighted.ToCharArray(); + int count = 0; for (int i = 0; i < contentArray.Length; i++) { char current = contentHightlighted[i]; if (current == '*') { flag = !flag; + count = count + 1; } else { if (flag) { - hightlightData.Add(i); + hightlightData.Add(i - count); } content.Append(current); } From 0737d290cb9dc2f968aa1cdd81dea299e7d63e5d Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 13:24:44 +0800 Subject: [PATCH 06/11] remove duplicate fuzzy match --- Plugins/Wox.Plugin.Program/Programs/UWP.cs | 31 +++++------------ Plugins/Wox.Plugin.Program/Programs/Win32.cs | 35 ++++++-------------- 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/Plugins/Wox.Plugin.Program/Programs/UWP.cs b/Plugins/Wox.Plugin.Program/Programs/UWP.cs index 7e928b3c5..dd4b0d241 100644 --- a/Plugins/Wox.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Wox.Plugin.Program/Programs/UWP.cs @@ -262,27 +262,12 @@ public class Application : IProgram public string LogoPath { get; set; } public UWP Package { get; set; } - 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 - return null; - } - var result = new Result { SubTitle = Package.Location, Icon = Logo, - Score = score, ContextData = this, Action = e => { @@ -291,23 +276,25 @@ public Result Result(string query, IPublicAPI api) } }; + string title; if (Description.Length >= DisplayName.Length && Description.Substring(0, DisplayName.Length) == DisplayName) { - result.Title = Description; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData; + title = Description; } else if (!string.IsNullOrEmpty(Description)) { - var title = $"{DisplayName}: {Description}"; - result.Title = title; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData; + title = $"{DisplayName}: {Description}"; } else { - result.Title = DisplayName; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, DisplayName).MatchData; + title = DisplayName; } + var match = StringMatcher.FuzzySearch(query, title); + result.Title = title; + result.Score = match.Score; + result.TitleHighlightData = match.MatchData; + return result; } diff --git a/Plugins/Wox.Plugin.Program/Programs/Win32.cs b/Plugins/Wox.Plugin.Program/Programs/Win32.cs index de4952533..8f6fdc2b7 100644 --- a/Plugins/Wox.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Wox.Plugin.Program/Programs/Win32.cs @@ -12,6 +12,7 @@ using Wox.Infrastructure; using Wox.Infrastructure.Logger; using Microsoft.WindowsAPICodePack.Shell; +using Windows.ApplicationModel.Resources; namespace Wox.Plugin.Program.Programs { @@ -33,29 +34,13 @@ public class Win32 : IProgram private const string ExeExtension = "exe"; private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); - 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 - return null; - } - var result = new Result { SubTitle = FullPath, IcoPath = IcoPath, - Score = score, ContextData = this, Action = e => { @@ -71,23 +56,23 @@ public Result Result(string query, IPublicAPI api) } }; - if (Description.Length >= Name.Length && - Description.Substring(0, Name.Length) == Name) + string title; + if (Description.Length >= Name.Length && Description.Substring(0, Name.Length) == Name) { - result.Title = Description; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData; + title = Description; } else if (!string.IsNullOrEmpty(Description)) { - var title = $"{Name}: {Description}"; - result.Title = title; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData; + title = $"{Name}: {Description}"; } else { - result.Title = Name; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData; + title = Name; } + var match = StringMatcher.FuzzySearch(query, title); + result.Title = title; + result.Score = match.Score; + result.TitleHighlightData = match.MatchData; return result; } From a4abefe3c9cb10e5923f06a2b18e18d9a8a62a74 Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 13:31:45 +0800 Subject: [PATCH 07/11] query won't need translate since query will always be engligh / pinyin --- Wox.Infrastructure/StringMatcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Wox.Infrastructure/StringMatcher.cs b/Wox.Infrastructure/StringMatcher.cs index 442c1e9dd..56b2f2438 100644 --- a/Wox.Infrastructure/StringMatcher.cs +++ b/Wox.Infrastructure/StringMatcher.cs @@ -62,7 +62,6 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption if (_alphabet != null) { - query = _alphabet.Translate(query); stringToCompare = _alphabet.Translate(stringToCompare); } From 0f3a55eca4c1fa93ce1f32d2dea918bf6e5b613d Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 14:30:33 +0800 Subject: [PATCH 08/11] extract query logic and remove useless match option --- Wox.Infrastructure/FuzzyMatcher.cs | 13 ++------ Wox.Infrastructure/StringMatcher.cs | 52 +++++++++-------------------- 2 files changed, 18 insertions(+), 47 deletions(-) diff --git a/Wox.Infrastructure/FuzzyMatcher.cs b/Wox.Infrastructure/FuzzyMatcher.cs index 49520e19d..0b69cdf7e 100644 --- a/Wox.Infrastructure/FuzzyMatcher.cs +++ b/Wox.Infrastructure/FuzzyMatcher.cs @@ -6,27 +6,20 @@ namespace Wox.Infrastructure public class FuzzyMatcher { private string query; - private MatchOption opt; - private FuzzyMatcher(string query, MatchOption opt) + private FuzzyMatcher(string query) { this.query = query.Trim(); - this.opt = opt; } public static FuzzyMatcher Create(string query) { - return new FuzzyMatcher(query, new MatchOption()); - } - - public static FuzzyMatcher Create(string query, MatchOption opt) - { - return new FuzzyMatcher(query, opt); + return new FuzzyMatcher(query); } public MatchResult Evaluate(string str) { - return StringMatcher.Instance.FuzzyMatch(query, str, opt); + return StringMatcher.Instance.FuzzyMatch(query, str); } } } diff --git a/Wox.Infrastructure/StringMatcher.cs b/Wox.Infrastructure/StringMatcher.cs index 56b2f2438..700efedd1 100644 --- a/Wox.Infrastructure/StringMatcher.cs +++ b/Wox.Infrastructure/StringMatcher.cs @@ -8,7 +8,6 @@ namespace Wox.Infrastructure { public class StringMatcher { - private readonly MatchOption _defaultMatchOption = new MatchOption(); public SearchPrecisionScore UserSettingSearchPrecision { get; set; } @@ -40,9 +39,17 @@ public static MatchResult FuzzySearch(string query, string stringToCompare) public MatchResult FuzzyMatch(string query, string stringToCompare) { - return FuzzyMatch(query, stringToCompare, _defaultMatchOption); - } + query = query.Trim(); + if (_alphabet != null) + { + stringToCompare = _alphabet.Translate(stringToCompare); + } + if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult(false, UserSettingSearchPrecision); + var fullStringToCompareWithoutCase = stringToCompare.ToLower(); + var queryWithoutCase = query.ToLower(); + return FuzzyMatchInternal(queryWithoutCase, fullStringToCompareWithoutCase); + } /// /// Current method: /// Character matching + substring matching; @@ -54,22 +61,9 @@ public MatchResult FuzzyMatch(string query, string stringToCompare) /// 6. Move onto the next substring's characters until all substrings are checked. /// 7. Consider success and move onto scoring if every char or substring without whitespaces matched /// - public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption opt) + public MatchResult FuzzyMatchInternal(string query, string stringToCompare) { - if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult (false, UserSettingSearchPrecision); - - query = query.Trim(); - - if (_alphabet != null) - { - stringToCompare = _alphabet.Translate(stringToCompare); - } - - var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare; - - var queryWithoutCase = opt.IgnoreCase ? query.ToLower() : query; - - var querySubstrings = queryWithoutCase.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + var querySubstrings = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int currentQuerySubstringIndex = 0; var currentQuerySubstring = querySubstrings[currentQuerySubstringIndex]; var currentQuerySubstringCharacterIndex = 0; @@ -83,9 +77,9 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption var indexList = new List(); - for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++) + for (var compareStringIndex = 0; compareStringIndex < stringToCompare.Length; compareStringIndex++) { - if (fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex]) + if (stringToCompare[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex]) { matchFoundInPreviousLoop = false; continue; @@ -109,7 +103,7 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption // in order to do so we need to verify all previous chars are part of the pattern var startIndexToVerify = compareStringIndex - currentQuerySubstringCharacterIndex; - if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, fullStringToCompareWithoutCase, currentQuerySubstring)) + if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, stringToCompare, currentQuerySubstring)) { matchFoundInPreviousLoop = true; @@ -287,20 +281,4 @@ private int ScoreAfterSearchPrecisionFilter(int rawScore) } } - public class MatchOption - { - /// - /// prefix of match char, use for hightlight - /// - [Obsolete("this is never used")] - public string Prefix { get; set; } = ""; - - /// - /// suffix of match char, use for hightlight - /// - [Obsolete("this is never used")] - public string Suffix { get; set; } = ""; - - public bool IgnoreCase { get; set; } = true; - } } From ece2f37561c8bb96a040868559dfdbbaee054e2c Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 14:39:14 +0800 Subject: [PATCH 09/11] cache for string match --- Wox.Infrastructure/StringMatcher.cs | 32 ++++++++++++++++---- Wox.Infrastructure/Wox.Infrastructure.csproj | 1 + 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Wox.Infrastructure/StringMatcher.cs b/Wox.Infrastructure/StringMatcher.cs index 700efedd1..07e3b2e3f 100644 --- a/Wox.Infrastructure/StringMatcher.cs +++ b/Wox.Infrastructure/StringMatcher.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel; +using System.Collections.Specialized; using System.Linq; +using System.Runtime.Caching; using static Wox.Infrastructure.StringMatcher; namespace Wox.Infrastructure @@ -12,10 +13,18 @@ public class StringMatcher public SearchPrecisionScore UserSettingSearchPrecision { get; set; } private readonly IAlphabet _alphabet; + private MemoryCache _cache; public StringMatcher(IAlphabet alphabet = null) { _alphabet = alphabet; + + NameValueCollection config = new NameValueCollection(); + config.Add("pollingInterval", "00:05:00"); + config.Add("physicalMemoryLimitPercentage", "1"); + config.Add("cacheMemoryLimitMegabytes", "50"); + + _cache = new MemoryCache("StringMatcherCache", config); } public static StringMatcher Instance { get; internal set; } @@ -48,7 +57,18 @@ public MatchResult FuzzyMatch(string query, string stringToCompare) if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult(false, UserSettingSearchPrecision); var fullStringToCompareWithoutCase = stringToCompare.ToLower(); var queryWithoutCase = query.ToLower(); - return FuzzyMatchInternal(queryWithoutCase, fullStringToCompareWithoutCase); + + string key = $"{queryWithoutCase}|{fullStringToCompareWithoutCase}"; + MatchResult match = _cache[key] as MatchResult; + if (match == null) + { + match = FuzzyMatchInternal(queryWithoutCase, fullStringToCompareWithoutCase); + CacheItemPolicy policy = new CacheItemPolicy(); + policy.SlidingExpiration = new TimeSpan(1, 0, 0); + _cache.Set(key, match, policy); + } + + return match; } /// /// Current method: @@ -136,7 +156,7 @@ public MatchResult FuzzyMatchInternal(string query, string stringToCompare) currentQuerySubstringCharacterIndex = 0; } } - + // proceed to calculate score if every char or substring without whitespaces matched if (allQuerySubstringsMatched) { @@ -145,10 +165,10 @@ public MatchResult FuzzyMatchInternal(string query, string stringToCompare) return new MatchResult(true, UserSettingSearchPrecision, indexList, score); } - return new MatchResult (false, UserSettingSearchPrecision); + return new MatchResult(false, UserSettingSearchPrecision); } - private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex, + private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex, string fullStringToCompareWithoutCase, string currentQuerySubstring) { var allMatch = true; @@ -163,7 +183,7 @@ private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQ return allMatch; } - + private static List GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex, int firstMatchIndexInWord, List indexList) { var updatedList = new List(); diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index 6f0bcd2eb..92e41471b 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -42,6 +42,7 @@ + From 552647631b30824dbb9b426c3c4a6170b5b4077f Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 14:46:17 +0800 Subject: [PATCH 10/11] adjust cache policy --- Wox.Infrastructure/StringMatcher.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Wox.Infrastructure/StringMatcher.cs b/Wox.Infrastructure/StringMatcher.cs index 07e3b2e3f..c88aab305 100644 --- a/Wox.Infrastructure/StringMatcher.cs +++ b/Wox.Infrastructure/StringMatcher.cs @@ -22,8 +22,7 @@ public StringMatcher(IAlphabet alphabet = null) NameValueCollection config = new NameValueCollection(); config.Add("pollingInterval", "00:05:00"); config.Add("physicalMemoryLimitPercentage", "1"); - config.Add("cacheMemoryLimitMegabytes", "50"); - + config.Add("cacheMemoryLimitMegabytes", "30"); _cache = new MemoryCache("StringMatcherCache", config); } @@ -64,7 +63,7 @@ public MatchResult FuzzyMatch(string query, string stringToCompare) { match = FuzzyMatchInternal(queryWithoutCase, fullStringToCompareWithoutCase); CacheItemPolicy policy = new CacheItemPolicy(); - policy.SlidingExpiration = new TimeSpan(1, 0, 0); + policy.SlidingExpiration = new TimeSpan(12, 0, 0); _cache.Set(key, match, policy); } From f379949c382d47587bf0115f23c65fe80c2d7de3 Mon Sep 17 00:00:00 2001 From: bao-qian Date: Mon, 18 May 2020 14:46:46 +0800 Subject: [PATCH 11/11] fine grained cache for pinyin --- Wox.Infrastructure/Alphabet.cs | 88 ++++++++++++++++------------------ 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/Wox.Infrastructure/Alphabet.cs b/Wox.Infrastructure/Alphabet.cs index ba21552b1..839fea9a6 100644 --- a/Wox.Infrastructure/Alphabet.cs +++ b/Wox.Infrastructure/Alphabet.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; +using System.Runtime.Caching; using System.Text; using hyjiacan.util.p4n; using hyjiacan.util.p4n.format; -using JetBrains.Annotations; using NLog; using Wox.Infrastructure.Logger; -using Wox.Infrastructure.Storage; using Wox.Infrastructure.UserSettings; namespace Wox.Infrastructure @@ -21,14 +21,20 @@ public interface IAlphabet public class Alphabet : IAlphabet { private readonly HanyuPinyinOutputFormat Format = new HanyuPinyinOutputFormat(); - private ConcurrentDictionary PinyinCache; - private BinaryStorage> _pinyinStorage; + private MemoryCache _cache; + private Settings _settings; private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); public void Initialize() { + NameValueCollection config = new NameValueCollection(); + config.Add("pollingInterval", "00:05:00"); + config.Add("physicalMemoryLimitPercentage", "1"); + config.Add("cacheMemoryLimitMegabytes", "30"); + _cache = new MemoryCache("StringMatcherCache", config); + _settings = Settings.Instance; InitializePinyinHelpers(); } @@ -36,21 +42,20 @@ public void Initialize() private void InitializePinyinHelpers() { Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); - - Logger.StopWatchNormal("Preload pinyin cache", () => - { - _pinyinStorage = new BinaryStorage>("Pinyin"); - PinyinCache = _pinyinStorage.TryLoad(new ConcurrentDictionary()); - - // force pinyin library static constructor initialize - PinyinHelper.toHanyuPinyinStringArray('T', Format); - }); - Logger.WoxInfo($"Number of preload pinyin combination<{PinyinCache.Count}>"); + PinyinHelper.toHanyuPinyinStringArray('T', Format); } - public string Translate(string str) + public string Translate(string key) { - return ConvertChineseCharactersToPinyin(str); + string pinyin = _cache[key] as string; + if (pinyin == null) + { + pinyin = ConvertChineseCharactersToPinyin(key); + CacheItemPolicy policy = new CacheItemPolicy(); + policy.SlidingExpiration = new TimeSpan(12, 0, 0); + _cache.Set(key, pinyin, policy); + } + return pinyin; } public string ConvertChineseCharactersToPinyin(string source) @@ -63,26 +68,21 @@ public string ConvertChineseCharactersToPinyin(string source) if (!ContainsChinese(source)) return source; - + var combination = PinyinCombination(source); - - var pinyinArray=combination.Select(x => string.Join("", x)); + + 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(); + var result = joinedSingleStringCombination.ToString(); + return result; } public void Save() { - if (!_settings.ShouldUsePinyin) - { - return; - } - _pinyinStorage.Save(PinyinCache); } private static string[] EmptyStringArray = new string[0]; @@ -122,32 +122,24 @@ public string[][] PinyinCombination(string characters) return Empty2DStringArray; } - if (!PinyinCache.ContainsKey(characters)) + var allPinyins = new List(); + foreach (var c in characters) { - var allPinyins = new List(); - foreach (var c in characters) + var pinyins = PinyinHelper.toHanyuPinyinStringArray(c, Format); + if (pinyins != null) { - 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 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]; } + + var combination = allPinyins.Aggregate(Combination).Select(c => c.Split(';')).ToArray(); + return combination; } public string Acronym(string[] pinyin)