Commit 387b0088 authored by Waqar Ahmed's avatar Waqar Ahmed
Browse files

Use QStringView instead of QChar*s and make sure we stay in range

parent c7b32006
......@@ -46,22 +46,27 @@ public:
{}
protected:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override
bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override
{
int l = source_left.data(KateQuickOpenModel::Score).toInt();
int r = source_right.data(KateQuickOpenModel::Score).toInt();
int l = sourceLeft.data(KateQuickOpenModel::Score).toInt();
int r = sourceRight.data(KateQuickOpenModel::Score).toInt();
return l < r;
}
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
{
if (filterStrings.isEmpty())
if (pattern.isEmpty())
return true;
const QString fileName = sourceModel()->index(sourceRow, 0, sourceParent).data().toString();
int score;
auto res = kfts::fuzzy_match(filterStrings.constData(), fileName.constData(), score);
// match
int score = 0;
auto res = kfts::fuzzy_match(pattern, fileName, score);
// store the score for sorting later
auto idx = sourceModel()->index(sourceRow, 0, sourceParent);
sourceModel()->setData(idx, score, KateQuickOpenModel::Score);
return res;
}
......@@ -69,12 +74,12 @@ public Q_SLOTS:
void setFilterText(const QString& text)
{
beginResetModel();
filterStrings = text;
pattern = text;
endResetModel();
}
private:
QString filterStrings;
QString pattern;
};
class QuickOpenStyleDelegate : public QStyledItemDelegate {
......
......@@ -16,65 +16,70 @@
*/
namespace kfts {
static bool fuzzy_match_simple(const QChar* pattern, const QChar* str);
static bool fuzzy_match(const QChar* pattern, const QChar* str, int & outScore);
static bool fuzzy_match(const QChar* pattern, const QChar* str, int & outScore, uint8_t * matches, int maxMatches);
static bool fuzzy_match_simple(const QStringView pattern, const QStringView str);
static bool fuzzy_match(const QStringView pattern, const QStringView str, int & outScore);
static bool fuzzy_match(const QStringView pattern, const QStringView str, int & outScore, uint8_t * matches, int maxMatches);
}
namespace kfts {
// Forward declarations for "private" implementation
namespace fuzzy_internal {
static bool fuzzy_match_recursive(const QChar* pattern, const QChar* str, int & outScore, const QChar* strBegin,
static bool fuzzy_match_recursive(QStringView::const_iterator pattern, QStringView::const_iterator str, int & outScore, const QStringView::const_iterator strBegin, const QStringView::const_iterator strEnd, const QStringView::const_iterator patternEnd,
uint8_t const * srcMatches, uint8_t * newMatches, int maxMatches, int nextMatch,
int & recursionCount, int recursionLimit);
int & recursionCount);
}
// Public interface
static bool fuzzy_match_simple(const QChar* pattern, const QChar* str)
static bool fuzzy_match_simple(const QStringView pattern, const QStringView str)
{
while (!pattern->isNull() && !str->isNull()) {
if (pattern->toLower() == str->toLower())
++pattern;
++str;
auto patternIt = pattern.cbegin();
for (auto strIt = str.cbegin(); strIt != str.cend() && patternIt != pattern.cend(); ++strIt) {
if (strIt->toLower() == patternIt->toLower())
++patternIt;
}
return pattern->isNull() ? true : false;
return patternIt == pattern.cend();
}
static bool fuzzy_match(const QChar* pattern, const QChar* str, int & outScore)
static bool fuzzy_match(const QStringView pattern, const QStringView str, int & outScore)
{
uint8_t matches[32];
uint8_t matches[256];
return fuzzy_match(pattern, str, outScore, matches, sizeof(matches));
}
static bool fuzzy_match(const QChar* pattern, const QChar* str, int & outScore, uint8_t * matches, int maxMatches)
static bool fuzzy_match(const QStringView pattern, const QStringView str, int & outScore, uint8_t * matches, int maxMatches)
{
int recursionCount = 0;
int recursionLimit = 10;
return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, maxMatches, 0, recursionCount, recursionLimit);
auto strIt = str.cbegin();
auto patternIt = pattern.cbegin();
const auto patternEnd = pattern.cend();
const auto strEnd = str.cend();
return fuzzy_internal::fuzzy_match_recursive(patternIt, strIt, outScore, strIt, strEnd, patternEnd, nullptr, matches, maxMatches, 0, recursionCount);
}
// Private implementation
static bool fuzzy_internal::fuzzy_match_recursive(const QChar* pattern,
const QChar* str,
int & outScore,
const QChar* strBegin,
uint8_t const * srcMatches,
uint8_t * matches,
static bool fuzzy_internal::fuzzy_match_recursive(QStringView::const_iterator pattern,
QStringView::const_iterator str,
int& outScore,
const QStringView::const_iterator strBegin,
const QStringView::const_iterator strEnd,
const QStringView::const_iterator patternEnd,
const uint8_t* srcMatches,
uint8_t* matches,
int maxMatches,
int nextMatch,
int & recursionCount,
int recursionLimit)
int& recursionCount)
{
// Count recursions
static constexpr int recursionLimit = 10;
++recursionCount;
if (recursionCount >= recursionLimit)
return false;
// Detect end of strings
if (pattern->isNull() || str->isNull())
if (pattern == patternEnd || str == strEnd)
return false;
// Recursion params
......@@ -84,7 +89,7 @@ namespace kfts {
// Loop through pattern and str looking for a match
bool first_match = true;
while (!pattern->isNull() && !str->isNull()) {
while (pattern != patternEnd && str != strEnd) {
// Found match
if (pattern->toLower() == str->toLower()) {
......@@ -102,7 +107,8 @@ namespace kfts {
// Recursive call that "skips" this match
uint8_t recursiveMatches[256];
int recursiveScore;
if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, sizeof(recursiveMatches), nextMatch, recursionCount, recursionLimit)) {
auto strNextChar = std::next(str);
if (fuzzy_match_recursive(pattern, strNextChar, recursiveScore, strBegin, strEnd, patternEnd, matches, recursiveMatches, sizeof(recursiveMatches), nextMatch, recursionCount)) {
// Pick best recursive score
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
......@@ -113,14 +119,14 @@ namespace kfts {
}
// Advance
matches[nextMatch++] = (uint8_t)(str - strBegin);
matches[nextMatch++] = (uint8_t)(std::distance(strBegin, str));
++pattern;
}
++str;
}
// Determine if full pattern was matched
bool matched = pattern->isNull() ? true : false;
bool matched = pattern == patternEnd ? true : false;
// Calculate score
if (matched) {
......@@ -134,7 +140,7 @@ namespace kfts {
static constexpr int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter
// Iterate str to end
while (!str->isNull())
while (str != strEnd)
++str;
// Initialize score
......@@ -147,7 +153,7 @@ namespace kfts {
outScore += penalty;
// Apply unmatched penalty
int unmatched = (int)(str - strBegin) - nextMatch;
const int unmatched = (int)(std::distance(strBegin, str)) - nextMatch;
outScore += unmatched_letter_penalty * unmatched;
// Apply ordering bonuses
......@@ -165,8 +171,8 @@ namespace kfts {
// Check for bonuses based on neighbor character value
if (currIdx > 0) {
// Camel case
QChar neighbor = strBegin[currIdx - 1];
QChar curr = strBegin[currIdx];
QChar neighbor = *(strBegin + currIdx - 1);
QChar curr = *(strBegin + currIdx);
if (neighbor.isLower() && curr.isUpper())
outScore += camel_bonus;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment