File 2000-better-krunner.patch of Package kf6-krunner

diff --git a/autotests/runnermanagerhistorytest.cpp b/autotests/runnermanagerhistorytest.cpp
index 05e2c46a8ed6d581d39a9e5361cb8d33613aaa57..67a394e106cea30f6d5156246f2bebf5d3f5fbc0 100644
--- a/autotests/runnermanagerhistorytest.cpp
+++ b/autotests/runnermanagerhistorytest.cpp
@@ -27,7 +27,7 @@ public:
     {
         __changeCountBeforeSaving = 1;
         QStandardPaths::setTestModeEnabled(true);
-        stateConfigFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + "krunnerstaterc";
+        stateConfigFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + "krunner/krunnerstaterc";
     }
 
 private:
@@ -119,28 +119,31 @@ void RunnerManagerHistoryTest::testRelevanceForOftenLaunched()
 
     launchQuery(QStringLiteral("foo"), manager.get());
 
-    const auto matches = manager->matches();
+    auto matches = manager->matches();
     QCOMPARE(matches.size(), 2);
     QCOMPARE(matches.at(0).id(), QStringLiteral("foo"));
     QCOMPARE(matches.at(1).id(), QStringLiteral("bar"));
-    QCOMPARE(matches.at(1).relevance(), 0.2);
+    QCOMPARE(manager->searchContext()->getLaunchCount(matches.at(0)), 5);
+    QCOMPARE(manager->searchContext()->getLaunchCount(matches.at(1)), 0);
 
-    QVERIFY(matches.at(0).relevance() > matches.at(1).relevance());
-    QVERIFY(matches.at(0).relevance() < 0.6); // 0.5 is the max we add as a bonus, 0.1 comes from the runner
     {
         KConfig cfg(stateConfigFile);
         cfg.group("PlasmaRunnerManager").writeEntry("LaunchCounts", QStringList{"5 foo", "5 bar"});
         cfg.sync();
-        KSharedConfig::openConfig(QStringLiteral("krunnerstaterc"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation)->reparseConfiguration();
+        KSharedConfig::openConfig(QStringLiteral("krunner/krunnerstaterc"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation)->reparseConfiguration();
     }
     manager = std::make_unique<RunnerManager>();
     manager->setAllowedRunners({QStringLiteral("fakerunnerplugin")});
     manager->loadRunner(KPluginMetaData::findPluginById(QStringLiteral("krunnertest"), QStringLiteral("fakerunnerplugin")));
 
     launchQuery(QStringLiteral("foo"), manager.get());
-    const auto newMatches = manager->matches();
-    QCOMPARE(newMatches.size(), 2);
-    QVERIFY(newMatches.at(0).relevance() < newMatches.at(1).relevance());
+
+    matches = manager->matches();
+    QCOMPARE(matches.size(), 2);
+    QCOMPARE(matches.at(0).id(), QStringLiteral("foo"));
+    QCOMPARE(matches.at(1).id(), QStringLiteral("bar"));
+    QCOMPARE(manager->searchContext()->getLaunchCount(matches.at(0)), 5);
+    QCOMPARE(manager->searchContext()->getLaunchCount(matches.at(1)), 5);
 }
 
 QTEST_MAIN(RunnerManagerHistoryTest)
diff --git a/src/model/resultsmodel.cpp b/src/model/resultsmodel.cpp
index 73c074a5883349e075690fae089012144a7bbcdc..78fc41cf9765a657b9526b02c7eff39c5cbe6f63 100644
--- a/src/model/resultsmodel.cpp
+++ b/src/model/resultsmodel.cpp
@@ -57,31 +57,32 @@ protected:
         Q_ASSERT((bool)sourceA.internalId() == (bool)sourceB.internalId());
         // Only check the favorite index if we compare categories. For individual matches, they will always be the same
         if (isCategoryComparison) {
-            const int favoriteA = sourceA.data(ResultsModel::FavoriteIndexRole).toInt();
-            const int favoriteB = sourceB.data(ResultsModel::FavoriteIndexRole).toInt();
-            bool isFavoriteA = favoriteA != -1;
-            bool isFavoriteB = favoriteB != -1;
-            if (isFavoriteA && !isFavoriteB) {
-                return false;
-            } else if (!isFavoriteA && isFavoriteB) {
+            const int indexA = sourceA.data(ResultsModel::FavoriteIndexRole).toInt();
+            const int indexB = sourceB.data(ResultsModel::FavoriteIndexRole).toInt();
+            if (indexA == indexB) {
+                return sourceA.data(ResultsModel::CategoryRelevanceRole).toReal() < sourceB.data(ResultsModel::CategoryRelevanceRole).toReal();
+            }
+            if (indexA < 0) {
                 return true;
             }
-
-            const int favoritesCount = sourceA.data(ResultsModel::FavoriteCountRole).toInt();
-            const double favoriteAMultiplicationFactor = (favoriteA ? 1 + ((favoritesCount - favoriteA) * 0.2) : 1);
-            const double typeA = sourceA.data(ResultsModel::CategoryRelevanceRole).toReal() * favoriteAMultiplicationFactor;
-            const double favoriteBMultiplicationFactor = (favoriteB ? 1 + ((favoritesCount - favoriteB) * 0.2) : 1);
-            const double typeB = sourceB.data(ResultsModel::CategoryRelevanceRole).toReal() * favoriteBMultiplicationFactor;
-            return typeA < typeB;
+            if (indexB < 0) {
+                return false;
+            }
+            return indexA > indexB;
         }
 
         const qreal relevanceA = sourceA.data(ResultsModel::RelevanceRole).toReal();
         const qreal relevanceB = sourceB.data(ResultsModel::RelevanceRole).toReal();
-
         if (!qFuzzyCompare(relevanceA, relevanceB)) {
             return relevanceA < relevanceB;
         }
 
+        const uint launchCountA = sourceA.data(ResultsModel::LaunchCountRole).toUInt();
+        const uint launchCountB = sourceB.data(ResultsModel::LaunchCountRole).toUInt();
+        if (launchCountA != launchCountB) {
+            return launchCountA < launchCountB;
+        }
+
         return QSortFilterProxyModel::lessThan(sourceA, sourceB);
     }
 
diff --git a/src/model/resultsmodel.h b/src/model/resultsmodel.h
index 0687acf1c9bebb95d7943275c7629d98d91f6a9d..f7dffd60f34722da4a9a476d627eec7d8c8e40d6 100644
--- a/src/model/resultsmodel.h
+++ b/src/model/resultsmodel.h
@@ -80,6 +80,7 @@ public:
         QueryMatchRole, /// @internal
         FavoriteIndexRole, /// @internal
         FavoriteCountRole, /// @internal
+        LaunchCountRole, /// @internal
     };
     Q_ENUM(Roles)
 
diff --git a/src/model/runnerresultsmodel.cpp b/src/model/runnerresultsmodel.cpp
index 19d8a2c8f05c1ea5255effc1920357eac62709f0..1390174e2e5f91c7b6e20e73453ea4a067cc13b1 100644
--- a/src/model/runnerresultsmodel.cpp
+++ b/src/model/runnerresultsmodel.cpp
@@ -43,6 +43,10 @@ void RunnerResultsModel::onMatchesChanged(const QList<KRunner::QueryMatch> &matc
     // of existing categories to avoid pointless model changes.
     QHash<QString /*category*/, QList<KRunner::QueryMatch>> newMatches;
     for (const auto &match : matches) {
+        if (match.text().trimmed().isEmpty()) {
+            continue;
+        }
+
         const QString category = match.matchCategory();
         newCategories.insert(category);
         newMatches[category].append(match);
@@ -285,6 +289,8 @@ QVariant RunnerResultsModel::data(const QModelIndex &index, int role) const
             return QVariant::fromValue(match.urls());
         case ResultsModel::MultiLineRole:
             return match.isMultiLine();
+        case ResultsModel::LaunchCountRole:
+            return m_manager->searchContext()->getLaunchCount(match);
         case ResultsModel::ActionsRole: {
             const auto actions = match.actions();
             QVariantList actionsList;
@@ -318,7 +324,9 @@ QVariant RunnerResultsModel::data(const QModelIndex &index, int role) const
             if (match.isValid()) {
                 const QString id = match.runner()->id();
                 int idx = m_favoriteIds.indexOf(id);
-                return idx;
+                if (idx >= 0) {
+                    return idx;
+                }
             }
         }
         // Any match that is not a favorite will have a greater index than an actual favorite
diff --git a/src/runnercontext.cpp b/src/runnercontext.cpp
index 7c8946877dcb6d2c6147efda65028e1d1f8a494f..dbd568c2cd296a8774a796569bd47d4d8ba6b8b1 100644
--- a/src/runnercontext.cpp
+++ b/src/runnercontext.cpp
@@ -55,22 +55,18 @@ public:
     void addMatch(const QueryMatch &match)
     {
         if (match.runner() && match.runner()->d->hasUniqueResults) {
-            if (uniqueIds.contains(match.id())) {
-                const QueryMatch &existentMatch = uniqueIds.value(match.id());
-                if (existentMatch.runner() && existentMatch.runner()->d->hasWeakResults) {
-                    // There is an existing match with the same ID and we are allowed to replace it
-                    matches.removeOne(existentMatch);
-                    matches.append(match);
+            auto iMatch = std::find_if(matches.cbegin(), matches.cend(), [&match](const QueryMatch &m) {
+                return m.id() == match.id();
+            });
+            if (iMatch != matches.cend()) {
+                if (*iMatch == match || !iMatch->runner() || !iMatch->runner()->d->hasWeakResults) {
+                    return;
                 }
-            } else {
-                // There is no existing match with the same id
-                uniqueIds.insert(match.id(), match);
-                matches.append(match);
+                matches.erase(iMatch);
             }
-        } else {
-            // Runner has the unique results property not set
-            matches.append(match);
         }
+
+        matches.append(match);
     }
 
     void matchesChanged()
@@ -84,12 +80,11 @@ public:
     QPointer<RunnerManager> m_manager;
     bool m_isValid = true;
     QList<QueryMatch> matches;
-    QHash<QString, int> launchCounts;
+    QHash<QString, uint> launchCounts;
     int changedLaunchCounts = 0; // We want to sync them while the app is running, but for each query it is overkill
     QString term;
     bool singleRunnerQueryMode = false;
     bool shouldIgnoreCurrentMatchForHistory = false;
-    QMap<QString, QueryMatch> uniqueIds;
     QString requestedText;
     int requestedCursorPosition = 0;
     qint64 queryStartTs = 0;
@@ -155,7 +150,6 @@ void RunnerContext::reset()
     d->term.clear();
     d->matchesChanged();
 
-    d->uniqueIds.clear();
     d->singleRunnerQueryMode = false;
     d->shouldIgnoreCurrentMatchForHistory = false;
 }
@@ -197,12 +191,7 @@ bool RunnerContext::addMatches(const QList<QueryMatch> &matches)
 
     {
         QWriteLocker locker(&d->lock);
-        for (QueryMatch match : matches) {
-            // Give previously launched matches a slight boost in relevance
-            // The boost smoothly saturates to 0.5;
-            if (int count = d->launchCounts.value(match.id())) {
-                match.setRelevance(match.relevance() + 0.5 * (1 - exp(-count * 0.3)));
-            }
+        for (const QueryMatch &match : matches) {
             d->addMatch(match);
         }
     }
@@ -244,6 +233,11 @@ void RunnerContext::ignoreCurrentMatchForHistory() const
     d->shouldIgnoreCurrentMatchForHistory = true;
 }
 
+uint RunnerContext::getLaunchCount(const QueryMatch &match) const
+{
+    return d->launchCounts.value(match.id());
+}
+
 bool RunnerContext::shouldIgnoreCurrentMatchForHistory() const
 {
     return d->shouldIgnoreCurrentMatchForHistory;
@@ -261,13 +255,14 @@ bool RunnerContext::shouldIgnoreCurrentMatchForHistory() const
  */
 void RunnerContext::restore(const KConfigGroup &config)
 {
-    const QStringList cfgList = config.readEntry("LaunchCounts", QStringList());
-
-    for (const QString &entry : cfgList) {
-        if (int idx = entry.indexOf(QLatin1Char(' ')); idx != -1) {
-            const int count = entry.mid(0, idx).toInt();
-            const QString id = entry.mid(idx + 1);
-            d->launchCounts[id] = count;
+    const QStringList launchCountsList = config.readEntry("LaunchCounts", QStringList());
+    for (const QString &entry : launchCountsList) {
+        if (int i = entry.indexOf(u' '); i != -1) {
+            const uint count = entry.first(i).toUInt();
+            const QString id = entry.mid(i).trimmed();
+            if (count > 0 && !id.isEmpty()) {
+                d->launchCounts[id] = count;
+            }
         }
     }
 }
@@ -277,14 +272,16 @@ void RunnerContext::save(KConfigGroup &config)
     if (d->changedLaunchCounts < __changeCountBeforeSaving) {
         return;
     }
+
     d->changedLaunchCounts = 0;
-    QStringList countList;
-    countList.reserve(d->launchCounts.size());
-    for (auto it = d->launchCounts.cbegin(), end = d->launchCounts.cend(); it != end; ++it) {
-        countList << QString::number(it.value()) + QLatin1Char(' ') + it.key();
+
+    QStringList launchCountsList;
+    launchCountsList.reserve(d->launchCounts.size());
+    for (auto i = d->launchCounts.cbegin(), end = d->launchCounts.cend(); i != end; ++i) {
+        launchCountsList << QStringLiteral("%1 %2").arg(i.value()).arg(i.key());
     }
 
-    config.writeEntry("LaunchCounts", countList);
+    config.writeEntry("LaunchCounts", launchCountsList);
     config.sync();
 }
 
diff --git a/src/runnercontext.h b/src/runnercontext.h
index 3cf5f5f7d31826b452897661f57b8a0f34b3cba1..2075fc67804a95e3ab04e6b84c33dbb7757b5b33 100644
--- a/src/runnercontext.h
+++ b/src/runnercontext.h
@@ -115,6 +115,8 @@ public:
      */
     void ignoreCurrentMatchForHistory() const;
 
+    uint getLaunchCount(const QueryMatch &match) const;
+
 private:
     KRUNNER_NO_EXPORT void increaseLaunchCount(const QueryMatch &match);
     KRUNNER_NO_EXPORT QString requestedQueryString() const;
diff --git a/src/runnermanager.cpp b/src/runnermanager.cpp
index e7c8c79d1911efb73a938e0d842a9abbbae5d0e4..c462a7b29e9bf77ef9e3572d76a74e8d9586f182 100644
--- a/src/runnermanager.cpp
+++ b/src/runnermanager.cpp
@@ -386,7 +386,7 @@ RunnerManager::RunnerManager(const KConfigGroup &pluginConfigGroup, const KConfi
 RunnerManager::RunnerManager(QObject *parent)
     : QObject(parent)
 {
-    auto defaultStatePtr = KSharedConfig::openConfig(QStringLiteral("krunnerstaterc"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation);
+    auto defaultStatePtr = KSharedConfig::openConfig(QStringLiteral("krunner/krunnerstaterc"), KConfig::NoGlobals, QStandardPaths::GenericDataLocation);
     auto configPtr = KSharedConfig::openConfig(QStringLiteral("krunnerrc"), KConfig::NoGlobals);
     d = std::make_unique<RunnerManagerPrivate>(configPtr->group(QStringLiteral("Plugins")),
                                                defaultStatePtr->group(QStringLiteral("PlasmaRunnerManager")),
openSUSE Build Service is sponsored by