diff options
Diffstat (limited to 'src/notmuchworker.cpp')
| -rw-r--r-- | src/notmuchworker.cpp | 77 |
1 files changed, 54 insertions, 23 deletions
diff --git a/src/notmuchworker.cpp b/src/notmuchworker.cpp index d41b54b..e5f5afa 100644 --- a/src/notmuchworker.cpp +++ b/src/notmuchworker.cpp @@ -362,12 +362,19 @@ static const int kSenderCountsMetaType = static const int kThreadDigestMetaType = qRegisterMetaType<ThreadDigest>("ThreadDigest"); +/// And for applyRule()'s answer, which crosses the queued ruleApplied +/// connection as a container. An unregistered one is dropped with only a +/// warning, and the window would then never build the rule's undo entry. +static const int kTagChangesMetaType = + qRegisterMetaType<QVector<TagChange>>("QVector<TagChange>"); + NotmuchWorker::NotmuchWorker(const QString ¬muchConfigPath, QObject *parent) : QObject(parent), m_configPath(notmuchConfigPath) { Q_UNUSED(kSortOrderMetaType); Q_UNUSED(kSenderCountsMetaType); Q_UNUSED(kThreadDigestMetaType); + Q_UNUSED(kTagChangesMetaType); } NotmuchWorker::~NotmuchWorker() @@ -1083,25 +1090,39 @@ void NotmuchWorker::applyTagsToThreads(const QStringList &threadIds, applyTags(TagChange{ messageIds, add, remove, description }); } -void NotmuchWorker::applyTagsToQuery(const QString &query, - const QStringList &add, - const QStringList &remove, - const QString &description) +void NotmuchWorker::applyRule(const QString &query, const QStringList &add, + const QStringList &remove, + const QString &description, quint64 token) { - if (!openReadOnly()) + QVector<TagChange> changes; + if (!openReadOnly()) { + emit ruleApplied(token, changes); return; + } QStringList messageIds; if (!collectMessageIds(m_db, SearchTerm::changes(query, add, remove), &messageIds)) { emit errorOccurred(QStringLiteral("Cannot resolve the rule's query")); + emit ruleApplied(token, changes); return; } - // Empty is not an error: the rule has nothing left to do. applyTags() - // returns at once on an empty list, and stays silent, as it must - // (CLAUDE.md, item 176). - applyTags(TagChange{ messageIds, add, remove, description }); + QList<TagChange> ops; + for (const QString &tag : add) + ops.append(TagChange{ messageIds, { tag }, {}, description }); + for (const QString &tag : remove) + ops.append(TagChange{ messageIds, {}, { tag }, description }); + + for (TagChange &op : ops) { + QStringList changed; + // An empty list writes nothing; writeTags() is not even asked to open. + if (!messageIds.isEmpty() && !writeTags(op, &changed)) + break; // errorOccurred() already emitted; report what landed + op.messageIds = changed; + changes.append(op); + } + emit ruleApplied(token, changes); } void NotmuchWorker::applyTags(const TagChange &change) @@ -1109,6 +1130,25 @@ void NotmuchWorker::applyTags(const TagChange &change) if (change.messageIds.isEmpty()) return; + QStringList changedIds; + if (!writeTags(change, &changedIds)) + return; + + // Nothing moved, so there is nothing to record as pending, nothing to sync + // and nothing to undo. Emitting an empty change would push an undo entry + // whose inverse adds a tag no message ever carried, which is item 176 one + // step further along. The same silence as the empty-id early return above, + // for the same reason. + if (changedIds.isEmpty()) + return; + + emit tagsApplied(TagChange{ changedIds, change.added, change.removed, + change.description }); +} + +bool NotmuchWorker::writeTags(const TagChange &change, QStringList *changedIds) +{ + // The read-only handle must be closed first: notmuch allows only one open // handle per process. close(); @@ -1139,7 +1179,7 @@ void NotmuchWorker::applyTags(const TagChange &change) .arg(QString::fromUtf8(error ? error : notmuch_status_to_string(status)))); free(error); - return; + return false; } // Item 176. What the caller ASKED for is not what the write does: a @@ -1149,8 +1189,8 @@ void NotmuchWorker::applyTags(const TagChange &change) // unread", measured on real mail as 2 unread becoming 43. With // maildir.synchronize_flags on, that rewrites filenames and reaches the // server. - QStringList changedIds; - changedIds.reserve(change.messageIds.size()); + changedIds->clear(); + changedIds->reserve(change.messageIds.size()); for (const QString &id : change.messageIds) { notmuch_message_t *raw = nullptr; @@ -1196,7 +1236,7 @@ void NotmuchWorker::applyTags(const TagChange &change) moves = moves || !before.contains(tag); moves = moves || !strippedOrigins.isEmpty(); if (moves) - changedIds.append(id); + changedIds->append(id); notmuch_message_freeze(message.get()); for (const QString &tag : change.removed) @@ -1215,16 +1255,7 @@ void NotmuchWorker::applyTags(const TagChange &change) notmuch_database_close(db); notmuch_database_destroy(db); - // Nothing moved, so there is nothing to record as pending, nothing to sync - // and nothing to undo. Emitting an empty change would push an undo entry - // whose inverse adds a tag no message ever carried, which is item 176 one - // step further along. The same silence as the empty-id early return above, - // for the same reason. - if (changedIds.isEmpty()) - return; - - emit tagsApplied(TagChange{ changedIds, change.added, change.removed, - change.description }); + return true; } void NotmuchWorker::moveMessages(const QStringList &messageIds, |
