diff --git a/Wox.Infrastructure/FuzzyMatcher.cs b/Wox.Infrastructure/FuzzyMatcher.cs new file mode 100644 index 0000000000..693bd9592a --- /dev/null +++ b/Wox.Infrastructure/FuzzyMatcher.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Wox.Infrastructure +{ + //From:http://crossplatform.net/sublime-text-ctrl-p-fuzzy-matching-in-python/ + public class FuzzyMatcher + { + private Regex reg = null; + private string rawQuery = ""; + + private FuzzyMatcher(string query) + { + this.rawQuery = query; + this.reg = GetPattern(query); + } + + private Regex GetPattern(string query) + { + var pattern = string.Join(".*?", query.ToCharArray().Select(x => Regex.Escape(x.ToString())).ToArray()); + return new Regex(pattern, RegexOptions.IgnoreCase); + } + + public int Score(string str) + { + var match = reg.Match(str); + if (!match.Success) + return 0; + + //a match found near the beginning of a string is scored more than a match found near the end + //a match is scored more if the characters in the patterns are closer to each other, while the score is lower if they are more spread out + var score = 100 * (this.rawQuery.Length + 1) / ((1 + match.Index) + (match.Length + 1)); + //a match with less characters assigning more weights + if (str.Length - this.rawQuery.Length < 5) + score = score + 20; + else if (str.Length - this.rawQuery.Length < 10) + score = score + 10; + + return score; + } + + public static FuzzyMatcher Create(string query) + { + return new FuzzyMatcher(query); + } + } +} diff --git a/Wox.Infrastructure/Wox.Infrastructure.csproj b/Wox.Infrastructure/Wox.Infrastructure.csproj index f538f42196..4aa8966552 100644 --- a/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -49,6 +49,7 @@ + diff --git a/Wox.Plugin.System/Programs.cs b/Wox.Plugin.System/Programs.cs index 37fd46b462..4c4d71854c 100644 --- a/Wox.Plugin.System/Programs.cs +++ b/Wox.Plugin.System/Programs.cs @@ -37,7 +37,8 @@ namespace Wox.Plugin.System { if (string.IsNullOrEmpty(query.RawQuery) || query.RawQuery.EndsWith(" ") || query.RawQuery.Length <= 1) return new List(); - List returnList = installedList.Where(o => MatchProgram(o, query)).ToList(); + var fuzzyMather = FuzzyMatcher.Create(query.RawQuery); + List returnList = installedList.Where(o => MatchProgram(o, fuzzyMather)).ToList(); returnList.ForEach(ScoreFilter); return returnList.Select(c => new Result() @@ -71,10 +72,12 @@ namespace Wox.Plugin.System }).ToList(); } - private bool MatchProgram(Program program, Query query) + private bool MatchProgram(Program program, FuzzyMatcher matcher) { - if (program.Title.ToLower().Contains(query.RawQuery.ToLower())) return true; - if (ChineseToPinYin.ToPinYin(program.Title).Replace(" ", "").ToLower().Contains(query.RawQuery.ToLower())) return true; + program.Score = matcher.Score(program.Title); + if (program.Score > 0) return true; + program.Score = matcher.Score(ChineseToPinYin.ToPinYin(program.Title).Replace(" ", "")); + if (program.Score > 0) return true; return false; } diff --git a/Wox.Test/FuzzyMatcherTest.cs b/Wox.Test/FuzzyMatcherTest.cs new file mode 100644 index 0000000000..8f92de72bd --- /dev/null +++ b/Wox.Test/FuzzyMatcherTest.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Wox.Infrastructure; + +namespace Wox.Test +{ + [TestFixture] + public class FuzzyMatcherTest + { + [Test] + public void MatchTest() + { + var sources = new List() + { + "file open in browser-test", + "Install Package", + "add new bsd", + "Inste", + "aac", + }; + + + var results = new List(); + foreach (var str in sources) + { + results.Add(new Plugin.Result() + { + Title = str, + Score = FuzzyMatcher.Create("inst").Score(str) + }); + } + + results = results.Where(x => x.Score > 0).OrderByDescending(x => x.Score).ToList(); + + Assert.IsTrue(results.Count == 3); + Assert.IsTrue(results[0].Title == "Inste"); + Assert.IsTrue(results[1].Title == "Install Package"); + Assert.IsTrue(results[2].Title == "file open in browser-test"); + } + } +} diff --git a/Wox.Test/Wox.Test.csproj b/Wox.Test/Wox.Test.csproj index 2544075926..f66ade2d3e 100644 --- a/Wox.Test/Wox.Test.csproj +++ b/Wox.Test/Wox.Test.csproj @@ -43,6 +43,7 @@ + @@ -55,6 +56,10 @@ {097B4AC0-74E9-4C58-BCF8-C69746EC8271} Python.Runtime + + {4FD29318-A8AB-4D8F-AA47-60BC241B8DA3} + Wox.Infrastructure + {8451ECDD-2EA4-4966-BB0A-7BBC40138E80} Wox.Plugin