@@ -157,23 +157,62 @@ object FuzzyFinder {
157157 ): MutableList <T > {
158158 if (query.isEmpty()) return itemsList.toMutableList()
159159
160- return if (prefs.enableFilterStrength) {
161- // 1. Calculate scores and filter by threshold
162- itemsList.mapNotNull { item ->
160+ val normalizedQuery = normalizeSearch(query)
161+
162+ // Keep original index as a final tie-breaker to preserve deterministic ordering.
163+ data class RankedItem <T >(
164+ val item : T ,
165+ val score : Int ,
166+ val startIndex : Int ,
167+ val startsWith : Boolean ,
168+ val wordStartsWith : Boolean ,
169+ val originalIndex : Int
170+ )
171+
172+ val rankedMatches = itemsList.mapIndexedNotNull { index, item ->
173+ val label = labelProvider(item)
174+ val normalizedTarget = normalizeTarget(label)
175+ val normalizedTargetCompact = normalizedTarget.replace(" " , emptyString())
176+ val startIndex = normalizedTargetCompact.indexOf(normalizedQuery)
177+ val startsWith = normalizedTargetCompact.startsWith(normalizedQuery)
178+ val wordStartsWith = normalizedTarget
179+ .split(Regex (" \\ s+" ))
180+ .any { it.startsWith(normalizedQuery) }
181+
182+ if (prefs.enableFilterStrength) {
163183 val score = scoreProvider(item, query)
164- AppLogger .d(loggerTag, " item: ${labelProvider(item)} | score: $score " )
165- if (score > prefs.filterStrength) item else null
166- }.toMutableList()
167- } else {
168- // 2. Simple Boolean matching
169- itemsList.filter { item ->
170- val target = labelProvider(item).lowercase()
171- if (prefs.searchFromStart) {
172- target.startsWith(query)
184+ AppLogger .d(loggerTag, " item: $label | score: $score " )
185+ val blockedByStartSetting = prefs.searchFromStart && ! startsWith
186+ if (score <= prefs.filterStrength || blockedByStartSetting) {
187+ null
173188 } else {
174- isMatch(target, query )
189+ RankedItem (item, score, startIndex, startsWith, wordStartsWith, index )
175190 }
176- }.toMutableList()
191+ } else {
192+ val targetLower = label.lowercase()
193+ val matched = if (prefs.searchFromStart) {
194+ targetLower.startsWith(query)
195+ } else {
196+ isMatch(targetLower, query)
197+ }
198+ if (! matched) {
199+ null
200+ } else {
201+ // Keep non-fuzzy mode deterministic but still prioritize stronger matches.
202+ RankedItem (item, 0 , startIndex, startsWith, wordStartsWith, index)
203+ }
204+ }
177205 }
206+
207+ return rankedMatches
208+ .sortedWith(
209+ compareByDescending<RankedItem <T >> { it.startsWith }
210+ .thenByDescending { it.wordStartsWith }
211+ .thenByDescending { it.score }
212+ .thenBy { if (it.startIndex >= 0 ) it.startIndex else Int .MAX_VALUE }
213+ .thenBy { it.originalIndex }
214+ )
215+ .map { it.item }
216+ .toMutableList()
178217 }
179218}
0 commit comments