#include "JumpRowProvider.h" #include #include #include #include #include "AppModel.h" #include "FuzzyMatch.h" namespace { // One scored snapshot row: the original map plus the score and the title taken // once, so the sort comparator never re-walks the variant. struct Hit { QVariant row; int score = 0; QString title; }; } // namespace JumpRowProvider::JumpRowProvider(QObject* parent) : FuzzyRowProvider(parent) {} void JumpRowProvider::setModel(AppModel* model) { if (m_model == model) { return; } // A QObject pointer, not shared data: the lock is for the snapshot, not for // the pointer itself. m_model = model; emit modelChanged(); invalidate(); } void JumpRowProvider::captureInputs() { // jumpCandidates() already returns plain maps; copy them so the worker never // dereferences the model or its live panes. m_snapshot = m_model != nullptr ? m_model->jumpCandidates() : QVariantList(); } QVariantList JumpRowProvider::rowsForQuery(const QString& query) { if (query.isEmpty()) { return m_snapshot; } std::vector hits; hits.reserve(static_cast(m_snapshot.size())); for (const QVariant& candidate : m_snapshot) { const QVariantMap map = candidate.toMap(); const QString title = map.value(QStringLiteral("title")).toString(); const int score = std::max(fuzzyScore(title, query), fuzzyScore(map.value(QStringLiteral("subtitle")).toString(), query)); if (score < 0) { continue; } hits.push_back(Hit{.row = candidate, .score = score, .title = title}); } std::ranges::sort(hits, [](const Hit& a, const Hit& b) { if (a.score != b.score) { return a.score > b.score; } return a.title < b.title; }); QVariantList out; out.reserve(static_cast(hits.size())); for (Hit& hit : hits) { out.append(std::move(hit.row)); } return out; }