diff --git a/src/base/utils/string.cpp b/src/base/utils/string.cpp index da42f79bb..3e9dbc952 100644 --- a/src/base/utils/string.cpp +++ b/src/base/utils/string.cpp @@ -54,6 +54,67 @@ QString Utils::String::wildcardToRegexPattern(const QString &pattern) return QRegularExpression::wildcardToRegularExpression(pattern, QRegularExpression::UnanchoredWildcardConversion); } +// namespace? private? +void toLookaheadRegex(QString &name, bool isPositive) { + name = QRegularExpression::escape(name); + + if(isPositive) + name.prepend(u"(?=.*").append(u")"); + else + name.prepend(u"(?!.*").append(u")"); +} + +QString Utils::String::parseFilter(const QString &filter) +{ + QString ret; + ret.reserve(256); + + ret.prepend(u"\\A"); // Start matching at beginning + + bool inQuotes = false; + bool excluded = false; + QString tmp; + for (const QChar c : filter) + { + if (c == u'"') // What about patterns without spaces between quotes, like `bla""bla` + { + inQuotes = !inQuotes; + continue; // Don't inlude quotes in pattern + } + else if (!inQuotes) // ` ` and `-` are left alone inside quotes + { + if (c == u' ') + { + if (!tmp.isEmpty()) + { + toLookaheadRegex(tmp, !excluded); + excluded = false; + + ret.append(tmp); + tmp.clear(); + } + + continue; + } + else if (c == u'-' && tmp.isEmpty()) // only exclude when `-` is at start of word + { + excluded = true; + continue; + } + } + + tmp.append(c); + } + + if (!tmp.isEmpty()) + { + toLookaheadRegex(tmp, !excluded); + ret.append(tmp); + } + + return ret; +} + QStringList Utils::String::splitCommand(const QString &command) { QStringList ret; diff --git a/src/base/utils/string.h b/src/base/utils/string.h index 6a72176f2..df43e05e3 100644 --- a/src/base/utils/string.h +++ b/src/base/utils/string.h @@ -44,6 +44,7 @@ namespace Utils::String { QString wildcardToRegexPattern(const QString &pattern); + QString parseFilter(const QString &filter); template T unquote(const T &str, const QString "es = u"\""_s) diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 85ac7b53a..3bd256fa8 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -1328,7 +1328,7 @@ void TransferListWidget::applyFilter(const QString &name, const TransferListMode { m_sortFilterModel->setFilterKeyColumn(type); const QString pattern = (Preferences::instance()->getRegexAsFilteringPatternForTransferList() - ? name : Utils::String::wildcardToRegexPattern(name)); + ? name : Utils::String::parseFilter(name)); m_sortFilterModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption)); } diff --git a/test/testutilsstring.cpp b/test/testutilsstring.cpp index 6c1c0c63c..140d08cb3 100644 --- a/test/testutilsstring.cpp +++ b/test/testutilsstring.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include "base/global.h" @@ -90,6 +91,57 @@ private slots: QCOMPARE(Utils::String::splitCommand(u" cmd.exe /d --arg2 \"arg3\" \"\" arg5 \"\"arg6 \"arg7 "_s) , QStringList({u"cmd.exe"_s, u"/d"_s, u"--arg2"_s, u"\"arg3\""_s, u"\"\""_s, u"arg5"_s, u"\"\"arg6"_s, u"\"arg7 "_s})); } + + void testparseFilter() const + { + QRegularExpression re0(Utils::String::parseFilter(u""_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u""_s.contains(re0)); + QVERIFY(u"a"_s.contains(re0)); + + QRegularExpression re1(Utils::String::parseFilter(u"a"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u"a"_s.contains(re1)); + QVERIFY(u"bab"_s.contains(re1)); + QVERIFY(!u"b"_s.contains(re1)); + + QRegularExpression re2(Utils::String::parseFilter(u"a b"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(!u"a"_s.contains(re2)); + QVERIFY(!u"b"_s.contains(re2)); + QVERIFY(u"a c b"_s.contains(re2)); + QVERIFY(u"b a"_s.contains(re2)); + + QRegularExpression re3(Utils::String::parseFilter(u"a -b"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u"a"_s.contains(re3)); + QVERIFY(!u"a b"_s.contains(re3)); + + QRegularExpression re4(Utils::String::parseFilter(uR"(a "b c")"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(!u"a c b"_s.contains(re4)); + QVERIFY(u"a b c"_s.contains(re4)); + + QRegularExpression re5(Utils::String::parseFilter(uR"(a -"b c")"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(!u"a b c"_s.contains(re5)); + QVERIFY(u"a c b"_s.contains(re5)); + + QRegularExpression re6(Utils::String::parseFilter(uR"(a "b -c")"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u"a b -c"_s.contains(re6)); + QVERIFY(!u"a b c"_s.contains(re6)); + + QRegularExpression re7(Utils::String::parseFilter(uR"(a-b)"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u"a-b"_s.contains(re7)); + QVERIFY(!u"a b"_s.contains(re7)); + QVERIFY(!u"a"_s.contains(re7)); + + QRegularExpression re8(Utils::String::parseFilter(uR"(.)"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u"."_s.contains(re8)); + QVERIFY(!u"a"_s.contains(re8)); + + QRegularExpression re9(Utils::String::parseFilter(uR"(*)"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u"*"_s.contains(re9)); + QVERIFY(!u"a"_s.contains(re9)); + + QRegularExpression re10(Utils::String::parseFilter(u"\"a"_s), QRegularExpression::CaseInsensitiveOption); + QVERIFY(u"a"_s.contains(re10)); + QVERIFY(!u"b"_s.contains(re10)); + } }; QTEST_APPLESS_MAIN(TestUtilsString)