diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-31 12:14:12 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-31 12:14:12 +0100 |
| commit | a30b4c2f2c0b11b7484ae91574755deb5b9a5be9 (patch) | |
| tree | 709b9adb5449bbabba897916a74fdc7d52145be4 /src/mainwindow.cpp | |
| parent | b1f6660b9f1471659ead81cf527d13d93dd57146 (diff) | |
| download | qtmaildir-a30b4c2f2c0b11b7484ae91574755deb5b9a5be9.tar.gz qtmaildir-a30b4c2f2c0b11b7484ae91574755deb5b9a5be9.zip | |
fix: credit a tag confirmation to the command whose tags it names
onTagsApplied() handed each confirmation to the head of the waiting
queue. The worker stays silent for a write that moved nothing, so when
one operation of a rule found nothing to do (another writer got there
between the count and the write), the next operation's ids were
credited to it, and its undo inverted the wrong tag on the wrong
messages: item 176 through a race.
A confirmation now goes to the first waiting command whose add and
remove sets equal the change's, and the commands ahead of it are
dropped with no ids, so their undo stays a no-op. Equal-tag commands,
the two halves of a mixed selection, still match in send order. No
match credits nobody. TagCommand carries its own add and remove to make
the comparison.
Items 176 and 212.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/mainwindow.cpp')
| -rw-r--r-- | src/mainwindow.cpp | 32 |
1 files changed, 29 insertions, 3 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8b1c537..9b1b874 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2889,6 +2889,23 @@ void MainWindow::applyRuleToExistingMail(const TagRule &rule) Q_ARG(quint64, m_pendingApplyGeneration)); } +QList<const TagCommand *> MainWindow::armTagCommandsForTesting( + const QList<QPair<QStringList, QStringList>> &ops) +{ + QList<const TagCommand *> commands; + m_undoStack.beginMacro(QStringLiteral("test")); + for (int i = 0; i < ops.size(); ++i) { + auto *command = new MessageTagCommand(this, {}, ops.at(i).first, + ops.at(i).second, + QStringLiteral("test")); + awaitTagConfirmation(command, i == 0); + m_undoStack.push(command); + commands.append(command); + } + m_undoStack.endMacro(); + return commands; +} + void MainWindow::confirmRuleApply(const QVector<int> &counts) { const TagRule rule = m_pendingApplyRule; @@ -5305,9 +5322,18 @@ void MainWindow::onTagsApplied(const TagChange &change) // instead turned the undo of "mark 44 read" into "mark 44 unread": 2 // unread became 43, on real mail, and maildir.synchronize_flags carried it // into the filenames. - if (!m_awaitingTagConfirmation.isEmpty()) { - TagCommand *command = m_awaitingTagConfirmation.takeFirst(); - command->addChangedMessageIds(change.messageIds); + // + // Matched on the tags, and the commands ahead of the match dropped: they + // were writes the worker found nothing to do for, and crediting them this + // write's ids would make their undo invert the wrong tag on the wrong + // messages. See awaitTagConfirmation(). + for (int i = 0; i < m_awaitingTagConfirmation.size(); ++i) { + TagCommand *command = m_awaitingTagConfirmation.at(i); + if (command->confirmedBy(change)) { + m_awaitingTagConfirmation.remove(0, i + 1); + command->addChangedMessageIds(change.messageIds); + break; + } } // Recorded here, where a write is CONFIRMED, rather than where one is sent: |
