mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #39 from jjw24/make_fuzzysearch_userfriendly
Make fuzzy search user friendly
This commit is contained in:
commit
8cdb5fe346
14 changed files with 347 additions and 134 deletions
|
|
@ -40,9 +40,8 @@ namespace Wox.Plugin.BrowserBookmark
|
|||
|
||||
if (!topResults)
|
||||
{
|
||||
// Since we mixed chrome and firefox bookmarks, we should order them again
|
||||
var fuzzyMatcher = FuzzyMatcher.Create(param);
|
||||
returnList = cachedBookmarks.Where(o => MatchProgram(o, fuzzyMatcher)).ToList();
|
||||
// Since we mixed chrome and firefox bookmarks, we should order them again
|
||||
returnList = cachedBookmarks.Where(o => MatchProgram(o, param)).ToList();
|
||||
returnList = returnList.OrderByDescending(o => o.Score).ToList();
|
||||
}
|
||||
|
||||
|
|
@ -61,11 +60,11 @@ namespace Wox.Plugin.BrowserBookmark
|
|||
}).ToList();
|
||||
}
|
||||
|
||||
private bool MatchProgram(Bookmark bookmark, FuzzyMatcher matcher)
|
||||
private bool MatchProgram(Bookmark bookmark, string queryString)
|
||||
{
|
||||
if ((bookmark.Score = matcher.Evaluate(bookmark.Name).Score) > 0) return true;
|
||||
if ((bookmark.Score = matcher.Evaluate(bookmark.PinyinName).Score) > 0) return true;
|
||||
if ((bookmark.Score = matcher.Evaluate(bookmark.Url).Score / 10) > 0) return true;
|
||||
if (StringMatcher.FuzzySearch(queryString, bookmark.Name, new MatchOption()).IsSearchPrecisionScoreMet()) return true;
|
||||
if ( StringMatcher.FuzzySearch(queryString, bookmark.PinyinName, new MatchOption()).IsSearchPrecisionScoreMet()) return true;
|
||||
if (StringMatcher.FuzzySearch(queryString, bookmark.Url, new MatchOption()).IsSearchPrecisionScoreMet()) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,14 +79,14 @@ namespace Wox.Plugin.ControlPanel
|
|||
var scores = new List<int> {0};
|
||||
if (string.IsNullOrEmpty(item.LocalizedString))
|
||||
{
|
||||
var score1 = StringMatcher.Score(item.LocalizedString, query);
|
||||
var score1 = StringMatcher.FuzzySearch(query, item.LocalizedString).ScoreAfterSearchPrecisionFilter();
|
||||
var score2 = StringMatcher.ScoreForPinyin(item.LocalizedString, query);
|
||||
scores.Add(score1);
|
||||
scores.Add(score2);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(item.InfoTip))
|
||||
{
|
||||
var score1 = StringMatcher.Score(item.InfoTip, query);
|
||||
var score1 = StringMatcher.FuzzySearch(query, item.InfoTip).ScoreAfterSearchPrecisionFilter();
|
||||
var score2 = StringMatcher.ScoreForPinyin(item.InfoTip, query);
|
||||
scores.Add(score1);
|
||||
scores.Add(score2);
|
||||
|
|
|
|||
|
|
@ -240,9 +240,9 @@ namespace Wox.Plugin.Program.Programs
|
|||
|
||||
private int Score(string query)
|
||||
{
|
||||
var score1 = StringMatcher.Score(DisplayName, query);
|
||||
var score1 = StringMatcher.FuzzySearch(query, DisplayName).ScoreAfterSearchPrecisionFilter();
|
||||
var score2 = StringMatcher.ScoreForPinyin(DisplayName, query);
|
||||
var score3 = StringMatcher.Score(Description, query);
|
||||
var score3 = StringMatcher.FuzzySearch(query, Description).ScoreAfterSearchPrecisionFilter();
|
||||
var score4 = StringMatcher.ScoreForPinyin(Description, query);
|
||||
var score = new[] { score1, score2, score3, score4 }.Max();
|
||||
return score;
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ namespace Wox.Plugin.Program.Programs
|
|||
|
||||
private int Score(string query)
|
||||
{
|
||||
var score1 = StringMatcher.Score(Name, query);
|
||||
var score1 = StringMatcher.FuzzySearch(query, Name).ScoreAfterSearchPrecisionFilter();
|
||||
var score2 = StringMatcher.ScoreForPinyin(Name, query);
|
||||
var score3 = StringMatcher.Score(Description, query);
|
||||
var score3 = StringMatcher.FuzzySearch(query, Description).ScoreAfterSearchPrecisionFilter();
|
||||
var score4 = StringMatcher.ScoreForPinyin(Description, query);
|
||||
var score5 = StringMatcher.Score(ExecutableName, query);
|
||||
var score5 = StringMatcher.FuzzySearch(query, ExecutableName).ScoreAfterSearchPrecisionFilter();
|
||||
var score = new[] { score1, score2, score3, score4, score5 }.Max();
|
||||
return score;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ namespace Wox.Plugin.Sys
|
|||
var results = new List<Result>();
|
||||
foreach (var c in commands)
|
||||
{
|
||||
var titleScore = StringMatcher.Score(c.Title, query.Search);
|
||||
var subTitleScore = StringMatcher.Score(c.SubTitle, query.Search);
|
||||
var titleScore = StringMatcher.FuzzySearch(query.Search, c.Title).ScoreAfterSearchPrecisionFilter();
|
||||
var subTitleScore = StringMatcher.FuzzySearch(query.Search, c.SubTitle).ScoreAfterSearchPrecisionFilter();
|
||||
var score = Math.Max(titleScore, subTitleScore);
|
||||
if (score > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Wox.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// refer to https://github.com/mattyork/fuzzy
|
||||
/// </summary>
|
||||
[Obsolete("This class is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
|
||||
public class FuzzyMatcher
|
||||
{
|
||||
private string query;
|
||||
|
|
@ -28,99 +26,7 @@ namespace Wox.Infrastructure
|
|||
|
||||
public MatchResult Evaluate(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(query)) return new MatchResult { Success = false };
|
||||
|
||||
var len = str.Length;
|
||||
var compareString = opt.IgnoreCase ? str.ToLower() : str;
|
||||
var pattern = opt.IgnoreCase ? query.ToLower() : query;
|
||||
|
||||
var sb = new StringBuilder(str.Length + (query.Length * (opt.Prefix.Length + opt.Suffix.Length)));
|
||||
var patternIdx = 0;
|
||||
var firstMatchIndex = -1;
|
||||
var lastMatchIndex = 0;
|
||||
char ch;
|
||||
for (var idx = 0; idx < len; idx++)
|
||||
{
|
||||
ch = str[idx];
|
||||
if (compareString[idx] == pattern[patternIdx])
|
||||
{
|
||||
if (firstMatchIndex < 0)
|
||||
firstMatchIndex = idx;
|
||||
lastMatchIndex = idx + 1;
|
||||
|
||||
sb.Append(opt.Prefix + ch + opt.Suffix);
|
||||
patternIdx += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(ch);
|
||||
}
|
||||
|
||||
// match success, append remain char
|
||||
if (patternIdx == pattern.Length && (idx + 1) != compareString.Length)
|
||||
{
|
||||
sb.Append(str.Substring(idx + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return rendered string if we have a match for every char
|
||||
if (patternIdx == pattern.Length)
|
||||
{
|
||||
return new MatchResult
|
||||
{
|
||||
Success = true,
|
||||
Value = sb.ToString(),
|
||||
Score = CalScore(str, firstMatchIndex, lastMatchIndex - firstMatchIndex)
|
||||
};
|
||||
}
|
||||
|
||||
return new MatchResult { Success = false };
|
||||
return StringMatcher.FuzzySearch(query, str, opt);
|
||||
}
|
||||
|
||||
private int CalScore(string str, int firstIndex, int matchLen)
|
||||
{
|
||||
//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 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
|
||||
//a match with less characters assigning more weights
|
||||
if (str.Length - query.Length < 5)
|
||||
score = score + 20;
|
||||
else if (str.Length - query.Length < 10)
|
||||
score = score + 10;
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
|
||||
public class MatchResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public int Score { get; set; }
|
||||
/// <summary>
|
||||
/// hightlight string
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class MatchOption
|
||||
{
|
||||
public MatchOption()
|
||||
{
|
||||
Prefix = "";
|
||||
Suffix = "";
|
||||
IgnoreCase = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// prefix of match char, use for hightlight
|
||||
/// </summary>
|
||||
public string Prefix { get; set; }
|
||||
/// <summary>
|
||||
/// suffix of match char, use for hightlight
|
||||
/// </summary>
|
||||
public string Suffix { get; set; }
|
||||
|
||||
public bool IgnoreCase { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Wox.Infrastructure;
|
||||
using System.Text;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Infrastructure.UserSettings;
|
||||
|
||||
namespace Wox.Infrastructure
|
||||
{
|
||||
public static class StringMatcher
|
||||
{
|
||||
public static string UserSettingSearchPrecision { get; set; }
|
||||
|
||||
[Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
|
||||
public static int Score(string source, string target)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target))
|
||||
{
|
||||
FuzzyMatcher matcher = FuzzyMatcher.Create(target);
|
||||
var score = matcher.Evaluate(source).Score;
|
||||
return score;
|
||||
return FuzzySearch(target, source, new MatchOption()).Score;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -22,6 +23,108 @@ namespace Wox.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
[Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
|
||||
public static bool IsMatch(string source, string target)
|
||||
{
|
||||
return FuzzySearch(target, source, new MatchOption()).Score > 0;
|
||||
}
|
||||
|
||||
public static MatchResult FuzzySearch(string query, string stringToCompare)
|
||||
{
|
||||
return FuzzySearch(query, stringToCompare, new MatchOption());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// refer to https://github.com/mattyork/fuzzy
|
||||
/// </summary>
|
||||
public static MatchResult FuzzySearch(string query, string stringToCompare, MatchOption opt)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult { Success = false };
|
||||
|
||||
query.Trim();
|
||||
|
||||
var len = stringToCompare.Length;
|
||||
var compareString = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare;
|
||||
var pattern = opt.IgnoreCase ? query.ToLower() : query;
|
||||
|
||||
var sb = new StringBuilder(stringToCompare.Length + (query.Length * (opt.Prefix.Length + opt.Suffix.Length)));
|
||||
var patternIdx = 0;
|
||||
var firstMatchIndex = -1;
|
||||
var lastMatchIndex = 0;
|
||||
char ch;
|
||||
for (var idx = 0; idx < len; idx++)
|
||||
{
|
||||
ch = stringToCompare[idx];
|
||||
if (compareString[idx] == pattern[patternIdx])
|
||||
{
|
||||
if (firstMatchIndex < 0)
|
||||
firstMatchIndex = idx;
|
||||
lastMatchIndex = idx + 1;
|
||||
|
||||
sb.Append(opt.Prefix + ch + opt.Suffix);
|
||||
patternIdx += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(ch);
|
||||
}
|
||||
|
||||
// match success, append remain char
|
||||
if (patternIdx == pattern.Length && (idx + 1) != compareString.Length)
|
||||
{
|
||||
sb.Append(stringToCompare.Substring(idx + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return rendered string if we have a match for every char
|
||||
if (patternIdx == pattern.Length)
|
||||
{
|
||||
return new MatchResult
|
||||
{
|
||||
Success = true,
|
||||
Value = sb.ToString(),
|
||||
Score = CalScore(query, stringToCompare, firstMatchIndex, lastMatchIndex - firstMatchIndex)
|
||||
};
|
||||
}
|
||||
|
||||
return new MatchResult { Success = false };
|
||||
}
|
||||
|
||||
private static int CalScore(string query, string stringToCompare, int firstIndex, int matchLen)
|
||||
{
|
||||
//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 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
|
||||
//a match with less characters assigning more weights
|
||||
if (stringToCompare.Length - query.Length < 5)
|
||||
score = score + 20;
|
||||
else if (stringToCompare.Length - query.Length < 10)
|
||||
score = score + 10;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public enum SearchPrecisionScore
|
||||
{
|
||||
Regular = 50,
|
||||
Low = 20,
|
||||
None = 0
|
||||
}
|
||||
|
||||
public static bool IsSearchPrecisionScoreMet(this MatchResult matchResult)
|
||||
{
|
||||
var precisionScore = (SearchPrecisionScore)Enum.Parse(typeof(SearchPrecisionScore),
|
||||
UserSettingSearchPrecision ?? SearchPrecisionScore.Regular.ToString());
|
||||
return matchResult.Score >= (int)precisionScore;
|
||||
}
|
||||
|
||||
public static int ScoreAfterSearchPrecisionFilter(this MatchResult matchResult)
|
||||
{
|
||||
return matchResult.IsSearchPrecisionScoreMet() ? matchResult.Score : 0;
|
||||
|
||||
}
|
||||
|
||||
public static int ScoreForPinyin(string source, string target)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target))
|
||||
|
|
@ -34,12 +137,12 @@ namespace Wox.Infrastructure
|
|||
|
||||
if (Alphabet.ContainsChinese(source))
|
||||
{
|
||||
FuzzyMatcher matcher = FuzzyMatcher.Create(target);
|
||||
var combination = Alphabet.PinyinComination(source);
|
||||
var pinyinScore = combination.Select(pinyin => matcher.Evaluate(string.Join("", pinyin)).Score)
|
||||
var combination = Alphabet.PinyinComination(source);
|
||||
var pinyinScore = combination
|
||||
.Select(pinyin => FuzzySearch(target, string.Join("", pinyin), new MatchOption()).Score)
|
||||
.Max();
|
||||
var acronymScore = combination.Select(Alphabet.Acronym)
|
||||
.Select(pinyin => matcher.Evaluate(pinyin).Score)
|
||||
var acronymScore = combination.Select(Alphabet.Acronym)
|
||||
.Select(pinyin => FuzzySearch(target, pinyin, new MatchOption()).Score)
|
||||
.Max();
|
||||
var score = Math.Max(pinyinScore, acronymScore);
|
||||
return score;
|
||||
|
|
@ -53,11 +156,37 @@ namespace Wox.Infrastructure
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MatchResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public int Score { get; set; }
|
||||
/// <summary>
|
||||
/// hightlight string
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class MatchOption
|
||||
{
|
||||
public MatchOption()
|
||||
{
|
||||
Prefix = "";
|
||||
Suffix = "";
|
||||
IgnoreCase = true;
|
||||
}
|
||||
|
||||
public static bool IsMatch(string source, string target)
|
||||
{
|
||||
return Score(source, target) > 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// prefix of match char, use for hightlight
|
||||
/// </summary>
|
||||
public string Prefix { get; set; }
|
||||
/// <summary>
|
||||
/// suffix of match char, use for hightlight
|
||||
/// </summary>
|
||||
public string Suffix { get; set; }
|
||||
|
||||
public bool IgnoreCase { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,17 @@ namespace Wox.Infrastructure.UserSettings
|
|||
public string ResultFontWeight { get; set; }
|
||||
public string ResultFontStretch { get; set; }
|
||||
|
||||
private string _querySearchPrecision { get; set; } = StringMatcher.SearchPrecisionScore.Regular.ToString();
|
||||
public string QuerySearchPrecision
|
||||
{
|
||||
get { return _querySearchPrecision; }
|
||||
set
|
||||
{
|
||||
_querySearchPrecision = value;
|
||||
StringMatcher.UserSettingSearchPrecision = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutoUpdates { get; set; } = false;
|
||||
|
||||
public double WindowLeft { get; set; }
|
||||
|
|
@ -63,7 +74,6 @@ namespace Wox.Infrastructure.UserSettings
|
|||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public LastQueryMode LastQueryMode { get; set; } = LastQueryMode.Selected;
|
||||
|
||||
}
|
||||
|
||||
public enum LastQueryMode
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Wox.Infrastructure;
|
||||
|
|
@ -9,6 +11,31 @@ namespace Wox.Test
|
|||
[TestFixture]
|
||||
public class FuzzyMatcherTest
|
||||
{
|
||||
public List<string> GetSearchStrings()
|
||||
=> new List<string>
|
||||
{
|
||||
"Chrome",
|
||||
"Choose which programs you want Windows to use for activities like web browsing, editing photos, sending e-mail, and playing music.",
|
||||
"Help cure hope raise on mind entity Chrome ",
|
||||
"Candy Crush Saga from King",
|
||||
"Uninstall or change programs on your computer",
|
||||
"Add, change, and manage fonts on your computer",
|
||||
"Last is chrome",
|
||||
"1111"
|
||||
};
|
||||
|
||||
public List<int> GetPrecisionScores()
|
||||
{
|
||||
var listToReturn = new List<int>();
|
||||
|
||||
Enum.GetValues(typeof(StringMatcher.SearchPrecisionScore))
|
||||
.Cast<StringMatcher.SearchPrecisionScore>()
|
||||
.ToList()
|
||||
.ForEach(x => listToReturn.Add((int)x));
|
||||
|
||||
return listToReturn;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchTest()
|
||||
{
|
||||
|
|
@ -28,7 +55,7 @@ namespace Wox.Test
|
|||
results.Add(new Result
|
||||
{
|
||||
Title = str,
|
||||
Score = FuzzyMatcher.Create("inst").Evaluate(str).Score
|
||||
Score = StringMatcher.FuzzySearch("inst", str, new MatchOption()).Score
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -39,5 +66,124 @@ namespace Wox.Test
|
|||
Assert.IsTrue(results[1].Title == "Install Package");
|
||||
Assert.IsTrue(results[2].Title == "file open in browser-test");
|
||||
}
|
||||
|
||||
[TestCase("Chrome")]
|
||||
public void WhenGivenNotAllCharactersFoundInSearchStringThenShouldReturnZeroScore(string searchString)
|
||||
{
|
||||
var compareString = "Can have rum only in my glass";
|
||||
|
||||
var scoreResult = StringMatcher.FuzzySearch(searchString, compareString, new MatchOption()).Score;
|
||||
|
||||
Assert.True(scoreResult == 0);
|
||||
}
|
||||
|
||||
[TestCase("chr")]
|
||||
[TestCase("chrom")]
|
||||
[TestCase("chrome")]
|
||||
[TestCase("cand")]
|
||||
[TestCase("cpywa")]
|
||||
[TestCase("ccs")]
|
||||
public void WhenGivenStringsAndAppliedPrecisionFilteringThenShouldReturnGreaterThanPrecisionScoreResults(string searchTerm)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
|
||||
foreach (var str in GetSearchStrings())
|
||||
{
|
||||
results.Add(new Result
|
||||
{
|
||||
Title = str,
|
||||
Score = StringMatcher.FuzzySearch(searchTerm, str, new MatchOption()).Score
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var precisionScore in GetPrecisionScores())
|
||||
{
|
||||
var filteredResult = results.Where(result => result.Score >= precisionScore).Select(result => result).OrderByDescending(x => x.Score).ToList();
|
||||
|
||||
Debug.WriteLine("");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("SEARCHTERM: " + searchTerm + ", GreaterThanSearchPrecisionScore: " + precisionScore);
|
||||
foreach (var item in filteredResult)
|
||||
{
|
||||
Debug.WriteLine("SCORE: " + item.Score.ToString() + ", FoundString: " + item.Title);
|
||||
}
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
Assert.IsFalse(filteredResult.Any(x => x.Score < precisionScore));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("chrome")]
|
||||
public void WhenGivenStringsForCalScoreMethodThenShouldReturnCurrentScoring(string searchTerm)
|
||||
{
|
||||
var searchStrings = new List<string>
|
||||
{
|
||||
"Chrome",//SCORE: 107
|
||||
"Last is chrome",//SCORE: 53
|
||||
"Help cure hope raise on mind entity Chrome",//SCORE: 21
|
||||
"Uninstall or change programs on your computer", //SCORE: 15
|
||||
"Candy Crush Saga from King"//SCORE: 0
|
||||
}
|
||||
.OrderByDescending(x => x)
|
||||
.ToList();
|
||||
|
||||
var results = new List<Result>();
|
||||
|
||||
foreach (var str in searchStrings)
|
||||
{
|
||||
results.Add(new Result
|
||||
{
|
||||
Title = str,
|
||||
Score = StringMatcher.FuzzySearch(searchTerm, str, new MatchOption()).Score
|
||||
});
|
||||
}
|
||||
|
||||
var orderedResults = results.OrderByDescending(x => x.Title).ToList();
|
||||
|
||||
Debug.WriteLine("");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("SEARCHTERM: " + searchTerm);
|
||||
foreach (var item in orderedResults)
|
||||
{
|
||||
Debug.WriteLine("SCORE: " + item.Score.ToString() + ", FoundString: " + item.Title);
|
||||
}
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
Assert.IsTrue(orderedResults[0].Score == 15 && orderedResults[0].Title == searchStrings[0]);
|
||||
Assert.IsTrue(orderedResults[1].Score == 53 && orderedResults[1].Title == searchStrings[1]);
|
||||
Assert.IsTrue(orderedResults[2].Score == 21 && orderedResults[2].Title == searchStrings[2]);
|
||||
Assert.IsTrue(orderedResults[3].Score == 107 && orderedResults[3].Title == searchStrings[3]);
|
||||
Assert.IsTrue(orderedResults[4].Score == 0 && orderedResults[4].Title == searchStrings[4]);
|
||||
}
|
||||
|
||||
[TestCase("goo", "Google Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("chr", "Google Chrome", (int)StringMatcher.SearchPrecisionScore.Low, true)]
|
||||
[TestCase("chr", "Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("chr", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
[TestCase("chr", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Low, true)]
|
||||
[TestCase("chr", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
[TestCase("chr", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.None, true)]
|
||||
[TestCase("ccs", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Low, true)]
|
||||
[TestCase("cand", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Regular, true)]
|
||||
[TestCase("cand", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, false)]
|
||||
public void WhenGivenDesiredPrecisionThenShouldReturnAllResultsGreaterOrEqual(string queryString, string compareString,
|
||||
int expectedPrecisionScore, bool expectedPrecisionResult)
|
||||
{
|
||||
var expectedPrecisionString = (StringMatcher.SearchPrecisionScore)expectedPrecisionScore;
|
||||
StringMatcher.UserSettingSearchPrecision = expectedPrecisionString.ToString();
|
||||
var matchResult = StringMatcher.FuzzySearch(queryString, compareString, new MatchOption());
|
||||
|
||||
Debug.WriteLine("");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine($"SearchTerm: {queryString} PrecisionLevelSetAt: {expectedPrecisionString} ({expectedPrecisionScore})");
|
||||
Debug.WriteLine($"SCORE: {matchResult.Score.ToString()}, ComparedString: {compareString}");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
var matchPrecisionResult = matchResult.IsSearchPrecisionScoreMet();
|
||||
Assert.IsTrue(matchPrecisionResult == expectedPrecisionResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
<Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ namespace Wox
|
|||
_settingsVM = new SettingWindowViewModel();
|
||||
_settings = _settingsVM.Settings;
|
||||
|
||||
StringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
|
||||
|
||||
PluginManager.LoadPlugins(_settings.PluginSettings);
|
||||
_mainVM = new MainViewModel(_settings);
|
||||
var window = new MainWindow(_settings, _mainVM);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@
|
|||
Checked="OnAutoStartupChecked" Unchecked="OnAutoStartupUncheck">
|
||||
<TextBlock Text="{DynamicResource autoUpdates}" />
|
||||
</CheckBox>
|
||||
<StackPanel Margin="10" Orientation="Horizontal">
|
||||
<TextBlock Text="Query Search Precision" />
|
||||
<ComboBox Margin="10 0 0 0" Width="120"
|
||||
ItemsSource="{Binding QuerySearchPrecisionStrings}"
|
||||
SelectedItem="{Binding Settings.QuerySearchPrecision}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="10" Orientation="Horizontal">
|
||||
<TextBlock Text="{DynamicResource lastQueryMode}" />
|
||||
<ComboBox Margin="10 0 0 0" Width="120"
|
||||
|
|
|
|||
|
|
@ -305,8 +305,8 @@ namespace Wox.ViewModel
|
|||
{
|
||||
var filtered = results.Where
|
||||
(
|
||||
r => StringMatcher.IsMatch(r.Title, query) ||
|
||||
StringMatcher.IsMatch(r.SubTitle, query)
|
||||
r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet()
|
||||
|| StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet()
|
||||
).ToList();
|
||||
ContextMenu.AddResults(filtered, id);
|
||||
}
|
||||
|
|
@ -348,8 +348,8 @@ namespace Wox.ViewModel
|
|||
{
|
||||
var filtered = results.Where
|
||||
(
|
||||
r => StringMatcher.IsMatch(r.Title, query) ||
|
||||
StringMatcher.IsMatch(r.SubTitle, query)
|
||||
r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet() ||
|
||||
StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet()
|
||||
).ToList();
|
||||
History.AddResults(filtered, id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,20 @@ namespace Wox.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
public List<string> QuerySearchPrecisionStrings
|
||||
{
|
||||
get
|
||||
{
|
||||
var precisionStrings = new List<string>();
|
||||
|
||||
var enumList = Enum.GetValues(typeof(StringMatcher.SearchPrecisionScore)).Cast<StringMatcher.SearchPrecisionScore>().ToList();
|
||||
|
||||
enumList.ForEach(x => precisionStrings.Add(x.ToString()));
|
||||
|
||||
return precisionStrings;
|
||||
}
|
||||
}
|
||||
|
||||
private Internationalization _translater => InternationalizationManager.Instance;
|
||||
public List<Language> Languages => _translater.LoadAvailableLanguages();
|
||||
public IEnumerable<int> MaxResultsRange => Enumerable.Range(2, 16);
|
||||
|
|
|
|||
Loading…
Reference in a new issue