Krita Source Code Documentation
Loading...
Searching...
No Matches
kfts::fuzzy_internal Namespace Reference

Functions

static bool fuzzy_match_recursive (QString::const_iterator pattern, QString::const_iterator str, int &outScore, const QString::const_iterator strBegin, const QString::const_iterator strEnd, const QString::const_iterator patternEnd, uint8_t const *srcMatches, uint8_t *newMatches, int maxMatches, int nextMatch, int &recursionCount, int seqBonus=15)
 

Function Documentation

◆ fuzzy_match_recursive()

static bool kfts::fuzzy_internal::fuzzy_match_recursive ( QString::const_iterator pattern,
QString::const_iterator str,
int & outScore,
const QString::const_iterator strBegin,
const QString::const_iterator strEnd,
const QString::const_iterator patternEnd,
uint8_t const * srcMatches,
uint8_t * newMatches,
int maxMatches,
int nextMatch,
int & recursionCount,
int seqBonus = 15 )
static

Definition at line 115 of file kfts_fuzzy_match.h.

127{
128 // Count recursions
129 static constexpr int recursionLimit = 10;
130 ++recursionCount;
131 if (recursionCount >= recursionLimit) {
132 return false;
133 }
134
135 // Detect end of strings
136 if (pattern == patternEnd || str == strEnd) {
137 return false;
138 }
139
140 // Recursion params
141 bool recursiveMatch = false;
142 uint8_t bestRecursiveMatches[256];
143 int bestRecursiveScore = 0;
144
145 // Loop through pattern and str looking for a match
146 bool first_match = true;
147 while (pattern != patternEnd && str != strEnd) {
148 // Found match
149 if (pattern->toLower() == str->toLower()) {
150 // Supplied matches buffer was too short
151 if (nextMatch >= maxMatches) {
152 return false;
153 }
154
155 // "Copy-on-Write" srcMatches into matches
156 if (first_match && srcMatches) {
157 memcpy(matches, srcMatches, nextMatch);
158 first_match = false;
159 }
160
161 // Recursive call that "skips" this match
162 uint8_t recursiveMatches[256];
163 int recursiveScore;
164 auto strNextChar = std::next(str);
165 if (fuzzy_match_recursive(pattern,
166 strNextChar,
167 recursiveScore,
168 strBegin,
169 strEnd,
170 patternEnd,
171 matches,
172 recursiveMatches,
173 sizeof(recursiveMatches),
174 nextMatch,
175 recursionCount)) {
176 // Pick best recursive score
177 if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
178 memcpy(bestRecursiveMatches, recursiveMatches, 256);
179 bestRecursiveScore = recursiveScore;
180 }
181 recursiveMatch = true;
182 }
183
184 // Advance
185 matches[nextMatch++] = (uint8_t)(std::distance(strBegin, str));
186 ++pattern;
187 }
188 ++str;
189 }
190
191 // Determine if full pattern was matched
192 bool matched = pattern == patternEnd ? true : false;
193
194 // Calculate score
195 if (matched) {
196 int sequential_bonus = seqBonus; // bonus for adjacent matches
197 static constexpr int separator_bonus = 30; // bonus if match occurs after a separator
198 static constexpr int camel_bonus = 30; // bonus if match is uppercase and prev is lower
199 static constexpr int first_letter_bonus = 15; // bonus if the first letter is matched
200
201 static constexpr int leading_letter_penalty = -5; // penalty applied for every letter in str before the first match
202 static constexpr int max_leading_letter_penalty = -15; // maximum penalty for leading letters
203 static constexpr int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter
204
205 // Iterate str to end
206 while (str != strEnd) {
207 ++str;
208 }
209
210 // Initialize score
211 outScore = 100;
212
213 // Apply leading letter penalty
214 int penalty = leading_letter_penalty * matches[0];
215 if (penalty < max_leading_letter_penalty) {
216 penalty = max_leading_letter_penalty;
217 }
218 outScore += penalty;
219
220 // Apply unmatched penalty
221 const int unmatched = (int)(std::distance(strBegin, str)) - nextMatch;
222 outScore += unmatched_letter_penalty * unmatched;
223
224 // Apply ordering bonuses
225 for (int i = 0; i < nextMatch; ++i) {
226 uint8_t currIdx = matches[i];
227
228 if (i > 0) {
229 uint8_t prevIdx = matches[i - 1];
230
231 // Sequential
232 if (currIdx == (prevIdx + 1)) {
233 outScore += sequential_bonus;
234 }
235 }
236
237 // Check for bonuses based on neighbor character value
238 if (currIdx > 0) {
239 // Camel case
240 QChar neighbor = *(strBegin + currIdx - 1);
241 QChar curr = *(strBegin + currIdx);
242 if (neighbor.isLower() && curr.isUpper()) {
243 outScore += camel_bonus;
244 }
245
246 // Separator
247 bool neighborSeparator = neighbor == QLatin1Char('_') || neighbor == QLatin1Char(' ');
248 if (neighborSeparator) {
249 outScore += separator_bonus;
250 }
251 } else {
252 // First letter
253 outScore += first_letter_bonus;
254 }
255 }
256 }
257
258 // Return best result
259 if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
260 // Recursive score is better than "this"
261 memcpy(matches, bestRecursiveMatches, maxMatches);
262 outScore = bestRecursiveScore;
263 return true;
264 } else if (matched) {
265 // "this" score is better than recursive
266 return true;
267 } else {
268 // no match
269 return false;
270 }
271}
static bool fuzzy_match_recursive(QString::const_iterator pattern, QString::const_iterator str, int &outScore, const QString::const_iterator strBegin, const QString::const_iterator strEnd, const QString::const_iterator patternEnd, uint8_t const *srcMatches, uint8_t *newMatches, int maxMatches, int nextMatch, int &recursionCount, int seqBonus=15)

References fuzzy_match_recursive().