diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-03 20:41:57 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-04 12:54:06 +0200 |
| commit | f6f11e5142911f0c4a57b08d6425d1cf99187627 (patch) | |
| tree | b5c91d17469cc6d75f67aff0e0fd99474245ab51 | |
| parent | ffe78f9c626ddb8d661930f911e8c1622cddfabb (diff) | |
| download | qtmaildir-f6f11e5142911f0c4a57b08d6425d1cf99187627.tar.gz qtmaildir-f6f11e5142911f0c4a57b08d6425d1cf99187627.zip | |
feat(completion): complete nothing inside a quoted literal
subject:"foo bar has the cursor in free text, where offering keywords
would be wrong.
| -rw-r--r-- | src/querycompleter.cpp | 15 | ||||
| -rw-r--r-- | tests/test_querycompleter.cpp | 18 |
2 files changed, 33 insertions, 0 deletions
diff --git a/src/querycompleter.cpp b/src/querycompleter.cpp index 1e86c1b..45ef581 100644 --- a/src/querycompleter.cpp +++ b/src/querycompleter.cpp @@ -20,6 +20,18 @@ namespace { +/// Whether the cursor sits inside a double-quoted literal. Counts quotes from +/// the start: an odd count before the cursor means the quote is still open. +bool insideQuotes(const QString &text, int cursor) +{ + int quotes = 0; + for (int i = 0; i < cursor; ++i) { + if (text.at(i) == QLatin1Char('"')) + ++quotes; + } + return (quotes % 2) != 0; +} + /// Start of the token the cursor sits in. The boundary is whitespace or '(', /// so "tag:inbox and su" has its last token starting at 14, not at 0. int tokenStart(const QString &text, int cursor) @@ -43,6 +55,9 @@ CompletionContext completionContext(const QString &text, int cursor) if (cursor < 0 || cursor > text.size()) return ctx; + if (insideQuotes(text, cursor)) + return ctx; // kind stays None + const int start = tokenStart(text, cursor); const QString token = text.mid(start, cursor - start); diff --git a/tests/test_querycompleter.cpp b/tests/test_querycompleter.cpp index 4621b19..c86edac 100644 --- a/tests/test_querycompleter.cpp +++ b/tests/test_querycompleter.cpp @@ -33,6 +33,8 @@ private slots: void valueReplaceSpanExcludesThePrefix(); void prefixIsLowercased(); void emptyValueAfterColonStillCompletes(); + void insideQuotesCompletesNothing(); + void afterClosedQuotesCompletesAgain(); }; void TestQueryCompleter::emptyTextCompletesPrefix() @@ -103,5 +105,21 @@ void TestQueryCompleter::emptyValueAfterColonStillCompletes() QCOMPARE(ctx.replaceLength, 0); } +void TestQueryCompleter::insideQuotesCompletesNothing() +{ + // subject:"foo bar| is a literal, not a keyword position. + const QString text = QStringLiteral("subject:\"foo bar"); + const CompletionContext ctx = completionContext(text, text.size()); + QCOMPARE(ctx.kind, CompletionContext::None); +} + +void TestQueryCompleter::afterClosedQuotesCompletesAgain() +{ + const QString text = QStringLiteral("subject:\"foo bar\" and ta"); + const CompletionContext ctx = completionContext(text, text.size()); + QCOMPARE(ctx.kind, CompletionContext::Prefix); + QCOMPARE(ctx.stem, QStringLiteral("ta")); +} + QTEST_MAIN(TestQueryCompleter) #include "test_querycompleter.moc" |
