diff --git a/kdevplatform/language/interfaces/abbreviations.h b/kdevplatform/language/interfaces/abbreviations.h --- a/kdevplatform/language/interfaces/abbreviations.h +++ b/kdevplatform/language/interfaces/abbreviations.h @@ -51,7 +51,7 @@ * @brief Matches a path against a list of search fragments. * @return -1 when no match is found, otherwise a positive integer, higher values mean lower quality */ -KDEVPLATFORMLANGUAGE_EXPORT int matchPathFilter(const Path& toFilter, const QStringList& text); +KDEVPLATFORMLANGUAGE_EXPORT int matchPathFilter(const Path& toFilter, const QStringList& text, const Path& prefixPath); } #endif diff --git a/kdevplatform/language/interfaces/abbreviations.cpp b/kdevplatform/language/interfaces/abbreviations.cpp --- a/kdevplatform/language/interfaces/abbreviations.cpp +++ b/kdevplatform/language/interfaces/abbreviations.cpp @@ -158,7 +158,7 @@ return matchedFragments == typedFragments.size(); } -int matchPathFilter(const Path &toFilter, const QStringList &text) +int matchPathFilter(const Path &toFilter, const QStringList &text, const Path &prefixPath) { enum PathFilterMatchQuality { NoMatch = -1, @@ -216,14 +216,20 @@ return NoMatch; } - // prefer matches whose last element starts with the filter - if (allMatched) { - return ExactMatch; + const int segmentMatchDistance = segments.size() - (pathIndex + 1); + const bool inPrefixPath = segmentMatchDistance > (segments.size() - prefixPath.segments().size()) + && prefixPath.isParentOf(toFilter); + // penalize matches that fall into the shared suffix + const int penalty = (inPrefixPath ) ? 1024 : 0; + + if (allMatched && !inPrefixPath) { + return ExactMatch + penalty; } else if (lastMatchIndex == 0) { - return StartMatch; + // prefer matches whose last element starts with the filter + return StartMatch + penalty; } else { // prefer matches closer to the end of the path - return OtherMatch + segments.size() - pathIndex; + return OtherMatch + segmentMatchDistance + penalty; } } diff --git a/kdevplatform/language/interfaces/quickopenfilter.h b/kdevplatform/language/interfaces/quickopenfilter.h --- a/kdevplatform/language/interfaces/quickopenfilter.h +++ b/kdevplatform/language/interfaces/quickopenfilter.h @@ -197,7 +197,8 @@ QVector> matches; for (int i = 0, c = filterBase.size(); i < c; ++i) { const auto& data = filterBase.at(i); - const auto matchQuality = matchPathFilter(static_cast(this)->itemPath(data), text); + const auto matchQuality = matchPathFilter(static_cast(this)->itemPath(data), text, + static_cast(this)->itemPrefixPath(data)); if (matchQuality == -1) { continue; } diff --git a/plugins/quickopen/projectfilequickopen.h b/plugins/quickopen/projectfilequickopen.h --- a/plugins/quickopen/projectfilequickopen.h +++ b/plugins/quickopen/projectfilequickopen.h @@ -104,6 +104,11 @@ { return data.path; } + + inline KDevelop::Path itemPrefixPath(const ProjectFile& data) const + { + return data.projectPath; + } }; /** diff --git a/plugins/quickopen/tests/quickopentestbase.h b/plugins/quickopen/tests/quickopentestbase.h --- a/plugins/quickopen/tests/quickopentestbase.h +++ b/plugins/quickopen/tests/quickopentestbase.h @@ -58,6 +58,10 @@ { return KDevelop::Path(data); } + KDevelop::Path itemPrefixPath(const QString& /*data*/) const + { + return KDevelop::Path(QStringLiteral("/home/user/project")); + } }; KDevelop::TestProject* getProjectWithFiles(int files); diff --git a/plugins/quickopen/tests/test_quickopen.cpp b/plugins/quickopen/tests/test_quickopen.cpp --- a/plugins/quickopen/tests/test_quickopen.cpp +++ b/plugins/quickopen/tests/test_quickopen.cpp @@ -247,6 +247,30 @@ QTest::newRow("prefer_multimatch_a_user") << a << QStringLiteral("user") << a; QTest::newRow("prefer_multimatch_b_user") << b << QStringLiteral("user") << a; } + { + const StringList a = { + QStringLiteral("/home/user/project/A/file"), + QStringLiteral("/home/user/project/B/project/A/file"), + QStringLiteral("/home/user/project/user/C/D/E"), + }; + const StringList b = { + QStringLiteral("/home/user/project/B/project/A/file"), + QStringLiteral("/home/user/project/A/file"), + }; + const StringList c = { + QStringLiteral("/home/user/project/user/C/D/E"), + QStringLiteral("/home/user/project/A/file"), + QStringLiteral("/home/user/project/B/project/A/file"), + }; + QTest::newRow("prefer_multimatch_a_project/file") << a << QStringLiteral("project/file") << b; + QTest::newRow("prefer_multimatch_b_project/file") << b << QStringLiteral("project/file") << b; + QTest::newRow("prefer_multimatch_a_project/le") << a << QStringLiteral("project/le") << b; + QTest::newRow("prefer_multimatch_b_project/le") << b << QStringLiteral("project/le") << b; + QTest::newRow("prefer_multimatch_a_project/a/file") << a << QStringLiteral("project/a/file") << b; + QTest::newRow("prefer_multimatch_b_project/a/file") << b << QStringLiteral("project/a/file") << b; + QTest::newRow("prefer_multimatch_a_project_user") << a << QStringLiteral("user") << c; + QTest::newRow("prefer_multimatch_c_project_user") << c << QStringLiteral("user") << c; + } } void TestQuickOpen::testProjectFileFilter()