9#include "kfuzzymatcher.h"
32 const uint8_t *srcMatches,
38 static constexpr int recursionLimit = 10;
40 static constexpr int maxMatches = 256;
44 if (recursionCount >= recursionLimit) {
49 if (pattern == patternEnd || str == strEnd) {
54 bool recursiveMatch =
false;
55 uint8_t bestRecursiveMatches[maxMatches];
56 int bestRecursiveScore = 0;
59 bool firstMatch =
true;
60 QChar currentPatternChar = toLower(*pattern);
62 bool matchingInSequence =
true;
63 while (pattern != patternEnd && str != strEnd) {
65 if (currentPatternChar == toLower(*str)) {
67 if (nextMatch >= maxMatches) {
72 if (firstMatch && srcMatches) {
73 memcpy(matches, srcMatches, nextMatch);
78 uint8_t recursiveMatches[maxMatches];
79 int recursiveScore = 0;
80 const auto strNextChar = std::next(str);
81 if (!matchingInSequence && match_recursive(pattern, strNextChar, recursiveScore, strBegin,
82 strEnd, patternEnd, matches, recursiveMatches,
83 nextMatch, totalMatches, recursionCount)) {
85 if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
86 memcpy(bestRecursiveMatches, recursiveMatches, maxMatches);
87 bestRecursiveScore = recursiveScore;
89 recursiveMatch =
true;
93 matches[nextMatch++] = (uint8_t)(std::distance(strBegin, str));
95 currentPatternChar = toLower(*pattern);
97 matchingInSequence =
false;
103 const bool matched = pattern == patternEnd;
107 static constexpr int sequentialBonus = 25;
108 static constexpr int separatorBonus = 25;
109 static constexpr int camelBonus = 25;
110 static constexpr int firstLetterBonus = 15;
112 static constexpr int leadingLetterPenalty = -5;
113 static constexpr int maxLeadingLetterPenalty = -15;
114 static constexpr int unmatchedLetterPenalty = -1;
116 static constexpr int nonBeginSequenceBonus = 10;
122 const int penalty = std::max(leadingLetterPenalty * matches[0], maxLeadingLetterPenalty);
127 const int unmatched = (int)(std::distance(strBegin, strEnd)) - nextMatch;
128 outScore += unmatchedLetterPenalty * unmatched;
130 uint8_t seqs[maxMatches] = {0};
134 for (
int i = 0; i < nextMatch; ++i) {
135 const uint8_t currIdx = matches[i];
138 const uint8_t prevIdx = matches[i - 1];
141 if (currIdx == (prevIdx + 1)) {
142 if (j > 0 && seqs[j - 1] == i - 1){
143 outScore += sequentialBonus;
147 outScore += nonBeginSequenceBonus;
155 const QChar neighbor = *(strBegin + currIdx - 1);
156 const QChar curr = *(strBegin + currIdx);
158 outScore += camelBonus;
163 if (neighborSeparator) {
164 outScore += separatorBonus;
168 outScore += firstLetterBonus;
174 totalMatches = nextMatch;
177 if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
179 memcpy(matches, bestRecursiveMatches, maxMatches);
180 outScore = bestRecursiveScore;
182 }
else if (matched) {
198 int recursionCount = 0;
200 auto strIt = str.
cbegin();
201 auto patternIt = pattern.
cbegin();
202 const auto patternEnd = pattern.
cend();
203 const auto strEnd = str.
cend();
206 return match_recursive(patternIt, strIt, outScore, strIt, strEnd, patternEnd,
nullptr, matches, 0, total, recursionCount);
213 auto patternIt = pattern.
cbegin();
222 bool lower = patternIt->isLower();
223 QChar cUp = lower ? patternIt->toUpper() : *patternIt;
224 QChar cLow = lower ? *patternIt : patternIt->toLower();
225 for (
auto strIt = str.
cbegin(); strIt != str.
cend() && patternIt != pattern.
cend(); ++strIt) {
226 if (*strIt == cLow || *strIt == cUp) {
228 lower = patternIt->isLower();
229 cUp = lower ? patternIt->toUpper() : *patternIt;
230 cLow = lower ? *patternIt : patternIt->toLower();
234 return patternIt == pattern.
cend();
242 const bool simpleMatch =
matchSimple(pattern, str);
254 uint8_t matches[256];
255 const bool matched = match_internal(pattern, str, score, matches);
257 result.
score = score;
268 int totalMatches = 0;
270 int recursionCount = 0;
272 auto strIt = str.
cbegin();
273 auto patternIt = pattern.
cbegin();
274 const auto patternEnd = pattern.
cend();
275 const auto strEnd = str.
cend();
277 uint8_t matches[256];
278 auto res = match_recursive(patternIt, strIt, score, strIt, strEnd, patternEnd,
nullptr, matches, 0, totalMatches, recursionCount);
280 if (!res && type == RangeType::FullyMatched) {
284 int previousMatch = 0;
285 for (
int i = 0; i < totalMatches; ++i) {
286 auto matchPos = matches[i];
292 if (!ranges.
isEmpty() && matchPos == previousMatch + 1) {
293 ranges.
last().length++;
300 previousMatch = matchPos;
KCOREADDONS_EXPORT bool matchSimple(QStringView pattern, QStringView str)
Simple fuzzy matching of chars in pattern with chars in str sequentially.
RangeType
The type of matches to consider when requesting for ranges.
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
This is the main function which does scored fuzzy matching.
KCOREADDONS_EXPORT QList< KFuzzyMatcher::Range > matchedRanges(QStringView pattern, QStringView str, RangeType type=RangeType::FullyMatched)
A function which returns the positions + lengths where the pattern matched inside the str.
bool isLower(char32_t ucs4)
bool isUpper(char32_t ucs4)
char32_t toLower(char32_t ucs4)
bool isEmpty() const const
void push_back(parameter_type value)
const_iterator cbegin() const const
const_iterator cend() const const
bool isEmpty() const const
The result of a fuzzy match.
int score
Score of this match.
bool matched
true if match was successful