Skip to content

Commit 59e61ce

Browse files
committed
Add Mapping to original string after translation. Not sure about the performance, but seems satisfying.
It requires at most n times loop (n: number of translated charater) mapping once.
1 parent 8a76ad0 commit 59e61ce

File tree

2 files changed

+108
-57
lines changed

2 files changed

+108
-57
lines changed

Flow.Launcher.Infrastructure/PinyinAlphabet.cs

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,85 @@
11
using System;
22
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
34
using System.Linq;
45
using System.Text;
56
using JetBrains.Annotations;
67
using Flow.Launcher.Infrastructure.UserSettings;
8+
using Microsoft.AspNetCore.Localization;
79
using ToolGood.Words.Pinyin;
810

911
namespace Flow.Launcher.Infrastructure
1012
{
13+
public class TranslationMapping
14+
{
15+
private bool constructed;
16+
17+
private List<int> originalIndexs = new List<int>();
18+
private List<int> translatedIndexs = new List<int>();
19+
private int translaedLength = 0;
20+
21+
public void AddNewIndex(int originalIndex, int translatedIndex, int length)
22+
{
23+
if (constructed)
24+
throw new InvalidOperationException("Mapping shouldn't be changed after constructed");
25+
26+
originalIndexs.Add(originalIndex);
27+
translatedIndexs.Add(translatedIndex);
28+
translatedIndexs.Add(translatedIndex + length);
29+
translaedLength += length - 1;
30+
}
31+
32+
public int? MapToOriginalIndex(int translatedIndex)
33+
{
34+
if (translatedIndex > translatedIndexs.Last())
35+
return translatedIndex - translaedLength - 1;
36+
37+
for (var i = 0; i < originalIndexs.Count; i++)
38+
{
39+
if (translatedIndex >= translatedIndexs[i * 2] && translatedIndex < translatedIndexs[i * 2 + 1])
40+
return originalIndexs[i];
41+
if (translatedIndex < translatedIndexs[i * 2])
42+
{
43+
int indexDiff = 0;
44+
for (int j = 0; j < i; j++)
45+
{
46+
indexDiff += translatedIndexs[i * 2 + 1] - translatedIndexs[i * 2] - 1;
47+
}
48+
49+
return translatedIndex - indexDiff;
50+
}
51+
}
52+
53+
return translatedIndex;
54+
}
55+
56+
public void endConstruct()
57+
{
58+
if (constructed)
59+
throw new InvalidOperationException("Mapping has already been constructed");
60+
constructed = true;
61+
}
62+
}
63+
1164
public interface IAlphabet
1265
{
13-
string Translate(string stringToTranslate);
66+
public (string translation, TranslationMapping map) Translate(string stringToTranslate);
1467
}
1568

1669
public class PinyinAlphabet : IAlphabet
1770
{
18-
private ConcurrentDictionary<string, string> _pinyinCache = new ConcurrentDictionary<string, string>();
71+
private ConcurrentDictionary<string, (string translation, TranslationMapping map)> _pinyinCache =
72+
new ConcurrentDictionary<string, (string translation, TranslationMapping map)>();
73+
74+
1975
private Settings _settings;
2076

2177
public void Initialize([NotNull] Settings settings)
2278
{
2379
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
2480
}
2581

26-
public string Translate(string content)
82+
public (string translation, TranslationMapping map) Translate(string content)
2783
{
2884
if (_settings.ShouldUsePinyin)
2985
{
@@ -34,21 +90,15 @@ public string Translate(string content)
3490
var resultList = WordsHelper.GetPinyinList(content);
3591

3692
StringBuilder resultBuilder = new StringBuilder();
37-
38-
for (int i = 0; i < resultList.Length; i++)
39-
{
40-
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
41-
resultBuilder.Append(resultList[i].First());
42-
}
43-
44-
resultBuilder.Append(' ');
93+
TranslationMapping map = new TranslationMapping();
4594

4695
bool pre = false;
4796

4897
for (int i = 0; i < resultList.Length; i++)
4998
{
5099
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
51100
{
101+
map.AddNewIndex(i, resultBuilder.Length, resultList[i].Length + 1);
52102
resultBuilder.Append(' ');
53103
resultBuilder.Append(resultList[i]);
54104
pre = true;
@@ -60,15 +110,18 @@ public string Translate(string content)
60110
pre = false;
61111
resultBuilder.Append(' ');
62112
}
113+
63114
resultBuilder.Append(resultList[i]);
64115
}
65116
}
66117

67-
return _pinyinCache[content] = resultBuilder.ToString();
118+
map.endConstruct();
119+
120+
return _pinyinCache[content] = (resultBuilder.ToString(), map);
68121
}
69122
else
70123
{
71-
return content;
124+
return (content, null);
72125
}
73126
}
74127
else
@@ -78,7 +131,7 @@ public string Translate(string content)
78131
}
79132
else
80133
{
81-
return content;
134+
return (content, null);
82135
}
83136
}
84137
}

Flow.Launcher.Infrastructure/StringMatcher.cs

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,12 @@ public MatchResult FuzzyMatch(string query, string stringToCompare)
4444
/// </summary>
4545
public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption opt)
4646
{
47-
if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult(false, UserSettingSearchPrecision);
47+
if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query))
48+
return new MatchResult(false, UserSettingSearchPrecision);
4849

4950
query = query.Trim();
50-
51-
stringToCompare = _alphabet?.Translate(stringToCompare) ?? stringToCompare;
52-
53-
// This also can be done by spliting the query
54-
55-
//(var spaceSplit, var upperSplit) = stringToCompare switch
56-
//{
57-
// string s when s.Contains(' ') => (s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(w => w.First()),
58-
// default(IEnumerable<char>)),
59-
// string s when s.Any(c => char.IsUpper(c)) && s.Any(c => char.IsLower(c)) =>
60-
// (null, Regex.Split(s, @"(?<!^)(?=[A-Z])").Select(w => w.First())),
61-
// _ => ((IEnumerable<char>)null, (IEnumerable<char>)null)
62-
//};
51+
TranslationMapping map;
52+
(stringToCompare, map) = _alphabet?.Translate(stringToCompare) ?? (stringToCompare, null);
6353

6454
var currentQueryIndex = 0;
6555
var acronymMatchData = new List<int>();
@@ -72,28 +62,24 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
7262
if (currentQueryIndex >= queryWithoutCase.Length)
7363
break;
7464

75-
if (compareIndex == 0 && queryWithoutCase[currentQueryIndex] == char.ToLower(stringToCompare[compareIndex]))
76-
{
77-
acronymMatchData.Add(compareIndex);
78-
currentQueryIndex++;
79-
continue;
80-
}
8165

8266
switch (stringToCompare[compareIndex])
8367
{
84-
case char c when compareIndex == 0 && queryWithoutCase[currentQueryIndex] == char.ToLower(stringToCompare[compareIndex])
85-
|| (char.IsUpper(c) && char.ToLower(c) == queryWithoutCase[currentQueryIndex])
86-
|| (char.IsWhiteSpace(c) && char.ToLower(stringToCompare[++compareIndex]) == queryWithoutCase[currentQueryIndex])
87-
|| (char.IsNumber(c) && c == queryWithoutCase[currentQueryIndex]):
88-
acronymMatchData.Add(compareIndex);
68+
case var c when (compareIndex == 0 && queryWithoutCase[currentQueryIndex] ==
69+
char.ToLower(stringToCompare[compareIndex]))
70+
|| (char.IsUpper(c) && char.ToLower(c) == queryWithoutCase[currentQueryIndex])
71+
|| (char.IsWhiteSpace(c) && char.ToLower(stringToCompare[++compareIndex]) ==
72+
queryWithoutCase[currentQueryIndex])
73+
|| (char.IsNumber(c) && c == queryWithoutCase[currentQueryIndex]):
74+
acronymMatchData.Add(map?.MapToOriginalIndex(compareIndex) ?? compareIndex);
8975
currentQueryIndex++;
9076
continue;
9177

92-
case char c when char.IsWhiteSpace(c):
78+
case var c when char.IsWhiteSpace(c):
9379
compareIndex++;
9480
acronymScore -= 10;
9581
break;
96-
case char c when char.IsUpper(c) || char.IsNumber(c):
82+
case var c when char.IsUpper(c) || char.IsNumber(c):
9783
acronymScore -= 10;
9884
break;
9985
}
@@ -105,7 +91,7 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
10591
var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare;
10692

10793

108-
var querySubstrings = queryWithoutCase.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
94+
var querySubstrings = queryWithoutCase.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
10995
int currentQuerySubstringIndex = 0;
11096
var currentQuerySubstring = querySubstrings[currentQuerySubstringIndex];
11197
var currentQuerySubstringCharacterIndex = 0;
@@ -120,17 +106,19 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
120106
var indexList = new List<int>();
121107
List<int> spaceIndices = new List<int>();
122108

123-
for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++)
109+
for (var compareStringIndex = 0;
110+
compareStringIndex < fullStringToCompareWithoutCase.Length;
111+
compareStringIndex++)
124112
{
125-
126113
// To maintain a list of indices which correspond to spaces in the string to compare
127114
// To populate the list only for the first query substring
128115
if (fullStringToCompareWithoutCase[compareStringIndex].Equals(' ') && currentQuerySubstringIndex == 0)
129116
{
130117
spaceIndices.Add(compareStringIndex);
131118
}
132119

133-
if (fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex])
120+
if (fullStringToCompareWithoutCase[compareStringIndex] !=
121+
currentQuerySubstring[currentQuerySubstringCharacterIndex])
134122
{
135123
matchFoundInPreviousLoop = false;
136124
continue;
@@ -154,14 +142,16 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
154142
// in order to do so we need to verify all previous chars are part of the pattern
155143
var startIndexToVerify = compareStringIndex - currentQuerySubstringCharacterIndex;
156144

157-
if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, fullStringToCompareWithoutCase, currentQuerySubstring))
145+
if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex,
146+
fullStringToCompareWithoutCase, currentQuerySubstring))
158147
{
159148
matchFoundInPreviousLoop = true;
160149

161150
// if it's the beginning character of the first query substring that is matched then we need to update start index
162151
firstMatchIndex = currentQuerySubstringIndex == 0 ? startIndexToVerify : firstMatchIndex;
163152

164-
indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex, firstMatchIndexInWord, indexList);
153+
indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex,
154+
firstMatchIndexInWord, indexList);
165155
}
166156
}
167157

@@ -174,11 +164,13 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
174164
if (currentQuerySubstringCharacterIndex == currentQuerySubstring.Length)
175165
{
176166
// if any of the substrings was not matched then consider as all are not matched
177-
allSubstringsContainedInCompareString = matchFoundInPreviousLoop && allSubstringsContainedInCompareString;
167+
allSubstringsContainedInCompareString =
168+
matchFoundInPreviousLoop && allSubstringsContainedInCompareString;
178169

179170
currentQuerySubstringIndex++;
180171

181-
allQuerySubstringsMatched = AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length);
172+
allQuerySubstringsMatched =
173+
AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length);
182174
if (allQuerySubstringsMatched)
183175
break;
184176

@@ -188,13 +180,16 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
188180
}
189181
}
190182

183+
191184
// proceed to calculate score if every char or substring without whitespaces matched
192185
if (allQuerySubstringsMatched)
193186
{
194187
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
195-
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
188+
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1,
189+
lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
196190

197-
return new MatchResult(true, UserSettingSearchPrecision, indexList, score);
191+
var resultList = indexList.Distinct().Select(x => map?.MapToOriginalIndex(x) ?? x).ToList();
192+
return new MatchResult(true, UserSettingSearchPrecision, resultList, score);
198193
}
199194

200195
return new MatchResult(false, UserSettingSearchPrecision);
@@ -209,14 +204,15 @@ private int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchInd
209204
}
210205
else
211206
{
212-
int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item)).Where(item => firstMatchIndex > item).FirstOrDefault();
207+
int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item))
208+
.FirstOrDefault(item => firstMatchIndex > item);
213209
int closestSpaceIndex = ind ?? -1;
214210
return closestSpaceIndex;
215211
}
216212
}
217213

218214
private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
219-
string fullStringToCompareWithoutCase, string currentQuerySubstring)
215+
string fullStringToCompareWithoutCase, string currentQuerySubstring)
220216
{
221217
var allMatch = true;
222218
for (int indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++)
@@ -231,7 +227,8 @@ private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQ
231227
return allMatch;
232228
}
233229

234-
private static List<int> GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex, int firstMatchIndexInWord, List<int> indexList)
230+
private static List<int> GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
231+
int firstMatchIndexInWord, List<int> indexList)
235232
{
236233
var updatedList = new List<int>();
237234

@@ -252,7 +249,8 @@ private static bool AllQuerySubstringsMatched(int currentQuerySubstringIndex, in
252249
return currentQuerySubstringIndex >= querySubstringsLength;
253250
}
254251

255-
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, bool allSubstringsContainedInCompareString)
252+
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen,
253+
bool allSubstringsContainedInCompareString)
256254
{
257255
// A match found near the beginning of a string is scored more than a match found near the end
258256
// A match is scored more if the characters in the patterns are closer to each other,
@@ -347,7 +345,7 @@ public bool IsSearchPrecisionScoreMet()
347345

348346
private bool IsSearchPrecisionScoreMet(int rawScore)
349347
{
350-
return rawScore >= (int)SearchPrecision;
348+
return rawScore >= (int) SearchPrecision;
351349
}
352350

353351
private int ScoreAfterSearchPrecisionFilter(int rawScore)
@@ -360,4 +358,4 @@ public class MatchOption
360358
{
361359
public bool IgnoreCase { get; set; } = true;
362360
}
363-
}
361+
}

0 commit comments

Comments
 (0)