Skip to content

Commit bf86397

Browse files
authored
Merge pull request #1 from Flow-Launcher/move_unicode_removal_into_main_loop
Unicode character removal- move normalization inside main loop & add early exists
2 parents 2ab6405 + c91a5d2 commit bf86397

1 file changed

Lines changed: 63 additions & 24 deletions

File tree

Flow.Launcher.Infrastructure/StringMatcher.cs

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Buffers;
33
using System.Collections.Generic;
44
using System.Linq;
5+
using System.Runtime.CompilerServices;
56
using CommunityToolkit.Mvvm.DependencyInjection;
67
using Flow.Launcher.Infrastructure.UserSettings;
78
using Flow.Launcher.Plugin.SharedModels;
@@ -91,18 +92,16 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
9192
int acronymsTotalCount = 0;
9293
int acronymsMatched = 0;
9394

94-
var fullStringToCompare = stringToCompare;
9595
var queryToCompare = query;
96+
bool ignoreAccents = _settings.IgnoreAccents;
97+
bool ignoreCase = opt.IgnoreCase;
9698

97-
if (_settings.IgnoreAccents)
99+
if (ignoreAccents)
98100
{
99-
fullStringToCompare = Normalize(fullStringToCompare);
100101
queryToCompare = Normalize(queryToCompare);
101102
}
102-
103-
if (opt.IgnoreCase)
103+
else if (ignoreCase)
104104
{
105-
fullStringToCompare = fullStringToCompare.ToLower();
106105
queryToCompare = queryToCompare.ToLower();
107106
}
108107

@@ -122,7 +121,7 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
122121
List<int> spaceIndices = new List<int>();
123122

124123
for (var compareStringIndex = 0;
125-
compareStringIndex < fullStringToCompare.Length;
124+
compareStringIndex < stringToCompare.Length;
126125
compareStringIndex++)
127126
{
128127
// If acronyms matching successfully finished, this gets the remaining not matched acronyms for score calculation
@@ -137,15 +136,25 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
137136
currentAcronymQueryIndex >= query.Length && allQuerySubstringsMatched)
138137
break;
139138

139+
char compareChar = stringToCompare[compareStringIndex];
140+
if (ignoreAccents)
141+
{
142+
compareChar = NormalizeChar(compareChar);
143+
}
144+
else if (ignoreCase)
145+
{
146+
compareChar = char.ToLower(compareChar);
147+
}
148+
140149
// To maintain a list of indices which correspond to spaces in the string to compare
141150
// To populate the list only for the first query substring
142-
if (fullStringToCompare[compareStringIndex] == ' ' && currentQuerySubstringIndex == 0)
151+
if (compareChar == ' ' && currentQuerySubstringIndex == 0)
143152
spaceIndices.Add(compareStringIndex);
144153

145154
// Acronym Match
146155
if (IsAcronym(stringToCompare, compareStringIndex))
147156
{
148-
if (fullStringToCompare[compareStringIndex] ==
157+
if (compareChar ==
149158
queryToCompare[currentAcronymQueryIndex])
150159
{
151160
acronymMatchData.Add(compareStringIndex);
@@ -158,7 +167,7 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
158167
if (IsAcronymCount(stringToCompare, compareStringIndex))
159168
acronymsTotalCount++;
160169

161-
if (allQuerySubstringsMatched || fullStringToCompare[compareStringIndex] !=
170+
if (allQuerySubstringsMatched || compareChar !=
162171
currentQuerySubstring[currentQuerySubstringCharacterIndex])
163172
{
164173
matchFoundInPreviousLoop = false;
@@ -185,7 +194,7 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
185194
var startIndexToVerify = compareStringIndex - currentQuerySubstringCharacterIndex;
186195

187196
if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex,
188-
fullStringToCompare, currentQuerySubstring))
197+
stringToCompare, currentQuerySubstring, ignoreAccents, ignoreCase))
189198
{
190199
matchFoundInPreviousLoop = true;
191200

@@ -344,20 +353,50 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
344353
['œ'] = 'o'
345354
};
346355

356+
private const char AccentRangeStart = '\u00DF';
357+
private const char AccentRangeEnd = '\u017E';
358+
private static readonly char[] AccentLookup = BuildAccentLookup();
359+
360+
private static char[] BuildAccentLookup()
361+
{
362+
var lookup = new char[AccentRangeEnd - AccentRangeStart + 1];
363+
foreach (var (key, value) in AccentMap)
364+
lookup[key - AccentRangeStart] = value;
365+
return lookup;
366+
}
367+
368+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
369+
public static char NormalizeChar(char c)
370+
{
371+
c = char.ToLowerInvariant(c);
372+
if (c >= AccentRangeStart && c <= AccentRangeEnd)
373+
{
374+
var mapped = AccentLookup[c - AccentRangeStart];
375+
if (mapped != 0) return mapped;
376+
}
377+
return c;
378+
}
379+
347380
public static string Normalize(string value)
348381
{
349382
if (string.IsNullOrEmpty(value)) return value;
383+
384+
int firstChange = -1;
385+
for (int i = 0; i < value.Length; i++)
386+
{
387+
if (NormalizeChar(value[i]) != value[i]) { firstChange = i; break; }
388+
}
389+
if (firstChange < 0) return value;
390+
350391
char[] arrayFromPool = null;
351392
Span<char> buffer = value.Length <= 512
352393
? stackalloc char[value.Length]
353394
: (arrayFromPool = ArrayPool<char>.Shared.Rent(value.Length));
354395
try
355396
{
356-
for (int i = 0; i < value.Length; i++)
357-
{
358-
var c = char.ToLowerInvariant(value[i]);
359-
buffer[i] = AccentMap.TryGetValue(c, out var mapped) ? mapped : c;
360-
}
397+
value.AsSpan(0, firstChange).CopyTo(buffer);
398+
for (int i = firstChange; i < value.Length; i++)
399+
buffer[i] = NormalizeChar(value[i]);
361400

362401
return new string(buffer.Slice(0, value.Length));
363402
}
@@ -415,19 +454,19 @@ private static int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstM
415454
}
416455

417456
private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
418-
string fullStringToCompare, string currentQuerySubstring)
457+
string stringToCompare, string currentQuerySubstring, bool ignoreAccents, bool ignoreCase)
419458
{
420-
var allMatch = true;
421459
for (int indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++)
422460
{
423-
if (fullStringToCompare[startIndexToVerify + indexToCheck] !=
424-
currentQuerySubstring[indexToCheck])
425-
{
426-
allMatch = false;
427-
}
461+
char c = stringToCompare[startIndexToVerify + indexToCheck];
462+
if (ignoreAccents) c = NormalizeChar(c);
463+
else if (ignoreCase) c = char.ToLower(c);
464+
465+
if (c != currentQuerySubstring[indexToCheck])
466+
return false;
428467
}
429468

430-
return allMatch;
469+
return true;
431470
}
432471

433472
private static List<int> GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex,

0 commit comments

Comments
 (0)