diff --git a/src/search/dolphinquery.h b/src/search/dolphinquery.h --- a/src/search/dolphinquery.h +++ b/src/search/dolphinquery.h @@ -48,13 +48,19 @@ /** @return Baloo::Query::includeFolder(), that is, the initial directory * for the query or an empty string if its a global search" */ QString includeFolder() const; + /** @return whether the query includes search in file content */ + bool hasContentSearch() const; + /** @return whether the query includes a filter by fileName */ + bool hasFileName() const; private: QUrl m_searchUrl; QString m_searchText; QString m_fileType; QStringList m_searchTerms; QString m_includeFolder; + bool m_hasContentSearch = false; + bool m_hasFileName = false; }; #endif //DOLPHINQUERY_H diff --git a/src/search/dolphinquery.cpp b/src/search/dolphinquery.cpp --- a/src/search/dolphinquery.cpp +++ b/src/search/dolphinquery.cpp @@ -19,6 +19,8 @@ #include "dolphinquery.h" +#include + #include #ifdef HAVE_BALOO #include @@ -43,6 +45,30 @@ } return false; } + + QString stripQuotes(const QString& text) + { + QString cleanedText = text; + if (!cleanedText.isEmpty() && cleanedText.at(0) == QLatin1Char('"')) { + cleanedText = cleanedText.mid(1); + } + if (!cleanedText.isEmpty() && cleanedText.back() == QLatin1Char('"')) { + cleanedText = cleanedText.mid(0, cleanedText.size() - 1); + } + return cleanedText; + } + + QStringList splitOutsideQuotes(const QString& text) + { + const QRegularExpression subTermsRegExp("([^ ]*\"[^\"]*\"|(?<= |^)[^ ]+(?= |$))"); + auto subTermsMatchIterator = subTermsRegExp.globalMatch(text); + + QStringList textParts; + while (subTermsMatchIterator.hasNext()) { + textParts << subTermsMatchIterator.next().captured(0); + } + return textParts; + } } DolphinQuery DolphinQuery::fromBalooSearchUrl(const QUrl& searchUrl) @@ -59,29 +85,35 @@ model.m_fileType = types.isEmpty() ? QString() : types.first(); QStringList textParts; + QString fileName; - const QStringList subTerms = query.searchString().split(' ', QString::SkipEmptyParts); + const QStringList subTerms = splitOutsideQuotes(query.searchString()); foreach (const QString& subTerm, subTerms) { - QString value; if (subTerm.startsWith(QLatin1String("filename:"))) { - value = subTerm.mid(9); + fileName = stripQuotes(subTerm.mid(9)); + if (!fileName.isEmpty()) { + model.m_hasFileName = true; + } + continue; } else if (isSearchTerm(subTerm)) { model.m_searchTerms << subTerm; continue; } else if (subTerm == QLatin1String("AND") && subTerm != subTerms.at(0) && subTerm != subTerms.back()) { continue; } else { - value = subTerm; + const QString cleanedTerm = stripQuotes(subTerm); + if (!cleanedTerm.isEmpty()) { + textParts << cleanedTerm; + model.m_hasContentSearch = true; + } } + } - if (!value.isEmpty() && value.at(0) == QLatin1Char('"')) { - value = value.mid(1); - } - if (!value.isEmpty() && value.back() == QLatin1Char('"')) { - value = value.mid(0, value.size() - 1); - } - if (!value.isEmpty()) { - textParts << value; + if (model.m_hasFileName) { + if (model.m_hasContentSearch) { + textParts << QStringLiteral("filename:\"%1\"").arg(fileName); + } else { + textParts << fileName; } } @@ -115,3 +147,13 @@ { return m_includeFolder; } + +bool DolphinQuery::hasContentSearch() const +{ + return m_hasContentSearch; +} + +bool DolphinQuery::hasFileName() const +{ + return m_hasFileName; +} diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -517,6 +517,12 @@ setText(query.text()); + if (query.hasContentSearch()) { + m_contentButton->setChecked(true); + } else if (query.hasFileName()) { + m_fileNameButton->setChecked(true); + } + m_facetsWidget->resetOptions(); m_facetsWidget->setFacetType(query.type()); const QStringList searchTerms = query.searchTerms(); diff --git a/src/tests/dolphinquerytest.cpp b/src/tests/dolphinquerytest.cpp --- a/src/tests/dolphinquerytest.cpp +++ b/src/tests/dolphinquerytest.cpp @@ -41,61 +41,66 @@ */ void DolphinSearchBoxTest::testBalooSearchParsing_data() { - const QString text = QStringLiteral("xyz"); - const QString filename = QStringLiteral("filename:\"xyz\""); + const QString text = QStringLiteral("abc xyz"); + const QString filename = QStringLiteral("filename:\"%1\"").arg(text); const QString rating = QStringLiteral("rating>=2"); const QString modified = QString("modified>=2019-08-07"); const QString tagA = QString("tag:tagA"); const QString tagB = QString("tag:tagB"); QTest::addColumn("searchString"); QTest::addColumn("expectedText"); QTest::addColumn("expectedTerms"); + QTest::addColumn("hasContent"); + QTest::addColumn("hasFileName"); // Test for "Content" - QTest::newRow("content") << text << text << QStringList(); - QTest::newRow("content/empty") << "" << "" << QStringList(); - QTest::newRow("content/singleQuote") << "\"" << "" << QStringList(); - QTest::newRow("content/doubleQuote") << "\"\"" << "" << QStringList(); + QTest::newRow("content") << text << text << QStringList() << true << false; + QTest::newRow("content/empty") << "" << "" << QStringList() << false << false; + QTest::newRow("content/singleQuote") << "\"" << "" << QStringList() << false << false; + QTest::newRow("content/doubleQuote") << "\"\"" << "" << QStringList() << false << false; - // Test for "Filename" - QTest::newRow("filename") << filename << text << QStringList(); - QTest::newRow("filename/empty") << "filename:" << "" << QStringList(); - QTest::newRow("filename/singleQuote") << "filename:\"" << "" << QStringList(); - QTest::newRow("filename/doubleQuote") << "filename:\"\"" << "" << QStringList(); + // Test for "FileName" + QTest::newRow("filename") << filename << text << QStringList() << false << true; + QTest::newRow("filename/empty") << "filename:" << "" << QStringList() << false << false; + QTest::newRow("filename/singleQuote") << "filename:\"" << "" << QStringList() << false << false; + QTest::newRow("filename/doubleQuote") << "filename:\"\"" << "" << QStringList() << false << false; + + // Combined content and filename search + QTest::newRow("content+filename") << text + " " + filename << text + " " + filename << QStringList() << true << true; // Test for rating - QTest::newRow("rating") << rating << "" << QStringList({rating}); - QTest::newRow("rating+content") << rating + " " + text << text << QStringList({rating}); - QTest::newRow("rating+filename") << rating + " " + filename << text << QStringList({rating}); + QTest::newRow("rating") << rating << "" << QStringList({rating}) << false << false; + QTest::newRow("rating+content") << rating + " " + text << text << QStringList({rating}) << true << false; + QTest::newRow("rating+filename") << rating + " " + filename << text << QStringList({rating}) << false << true; // Test for modified date - QTest::newRow("modified") << modified << "" << QStringList({modified}); - QTest::newRow("modified+content") << modified + " " + text << text << QStringList({modified}); - QTest::newRow("modified+filename") << modified + " " + filename << text << QStringList({modified}); + QTest::newRow("modified") << modified << "" << QStringList({modified}) << false << false; + QTest::newRow("modified+content") << modified + " " + text << text << QStringList({modified}) << true << false; + QTest::newRow("modified+filename") << modified + " " + filename << text << QStringList({modified}) << false << true; // Test for tags - QTest::newRow("tag") << tagA << "" << QStringList({tagA}); - QTest::newRow("tag/double") << tagA + " " + tagB << "" << QStringList({tagA, tagB}); - QTest::newRow("tag+content") << tagA + " " + text << text << QStringList({tagA}); - QTest::newRow("tag+filename") << tagA + " " + filename << text << QStringList({tagA}); - - // Combined tests - QTest::newRow("rating+modified") - << rating + " AND " + modified - << "" << QStringList({modified, rating}); + QTest::newRow("tag") << tagA << "" << QStringList({tagA}) << false << false; + QTest::newRow("tag/double") << tagA + " " + tagB << "" << QStringList({tagA, tagB}) << false << false; + QTest::newRow("tag+content") << tagA + " " + text << text << QStringList({tagA}) << true << false; + QTest::newRow("tag+filename") << tagA + " " + filename << text << QStringList({tagA}) << false << true; + // Combined search terms QTest::newRow("allTerms") << rating + " AND " + modified + " AND " + tagA + " AND " + tagB - << "" << QStringList({modified, rating, tagA, tagB}); + << "" << QStringList({modified, rating, tagA, tagB}) << false << false; QTest::newRow("allTerms+content") << rating + " AND " + modified + " " + text + " " + tagA + " AND " + tagB - << text << QStringList({modified, rating, tagA, tagB}); - + << text << QStringList({modified, rating, tagA, tagB}) << true << false; + QTest::newRow("allTerms+filename") << rating + " AND " + modified + " " + filename + " " + tagA + " AND " + tagB - << text << QStringList({modified, rating, tagA, tagB}); + << text << QStringList({modified, rating, tagA, tagB}) << false << true; + + QTest::newRow("allTerms+content+filename") + << text + " " + filename + " " + rating + " AND " + modified + " AND " + tagA + " AND " + tagB + << text + " " + filename << QStringList({modified, rating, tagA, tagB}) << true << true; } /** @@ -130,6 +135,8 @@ QFETCH(QString, searchString); QFETCH(QString, expectedText); QFETCH(QStringList, expectedTerms); + QFETCH(bool, hasContent); + QFETCH(bool, hasFileName); const QUrl testUrl = composeQueryUrl(searchString); const DolphinQuery query = DolphinQuery::fromBalooSearchUrl(testUrl); @@ -145,6 +152,10 @@ for (int i = 0; i < expectedTerms.count(); i++) { QCOMPARE(searchTerms.at(i), expectedTerms.at(i)); } + + // Check for filename and content detection + QCOMPARE(query.hasContentSearch(), hasContent); + QCOMPARE(query.hasFileName(), hasFileName); } QTEST_MAIN(DolphinSearchBoxTest)