22using System . Buffers ;
33using System . Collections . Generic ;
44using System . Linq ;
5+ using System . Runtime . CompilerServices ;
56using CommunityToolkit . Mvvm . DependencyInjection ;
67using Flow . Launcher . Infrastructure . UserSettings ;
78using 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 = '\u017D ' ;
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