This question was first raised on the mutt-users@mutt.org mailing list back in 2008:
Hello
how can I sync the mailbox after save a message to any folder like this:
macro s "<save-message>?"
the above works great, but after this save mutt should sync automatically. Like this:
macro s "<save-message>?<sync-mailbox>"
but, this doesn’t work, because mutt wait for any data from user and take then ync-mailbox> [sic] as mailbox-destination, which is wrong.
any tips?
Thank you.
Raphael
The Problem
But first, some context: Certain aspects of mutt’s design betray its 90s-era provenance. A particular thorn in my side has been the way it handles making changes to your mailstore: Anytime you delete, move, or duplicate a message, it doesn’t actually register that change until you sync-mailbox
(the $
key, by default).
This behavior can be useful if bandwidth is scarce and you’re accessing your email remotely — back in ’95, on a 28.8k modem, you might have wanted to be economical with the number of POP/IMAP requests you initiated. (Disclaimer: I was 9 years old in 1995, so I’m not actually speaking from firsthand experience. But one can speculate.)
In 2017, collecting CRUD operations and limiting syncs to conserve bandwidth is beyond miserly, to say nothing of the fact that many users (likely a majority) aren’t even accessing remote mailstores in the first place, but rather using mutt to manage a local mirror of their remote mailboxes.
The Fix
Looks easy enough
To automate the sync-mailbox
function, you have to manually remap each CRUD operation as a macro, adding a call to sync-mailbox
to the end. It’s hacky, but it’s all that can be done with the tools mutt gives you.
This is simple when you just want to delete-message
,
macro index,pager d "<delete-message><sync-mailbox>" "move message to trash"
but moving messages and saving copies to different folders is tricky: you want to press the keybinding, then manually select a folder, then have it sync. But as described by OP above, there’s no facility in mutt keybindings that says “wait until the user is done selecting a folder.”
Getting clever
So instead, what you have to do is this:
macro index s "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015\"\015<save-message>?" "save message to a mailbox"
This is starting to get messy, so let’s break it down and see what’s really happening. In the lines below, all backslash-escaping has been removed, and each nested macro has been extracted into the subsequent line.
-
Bind
s
to a macro that…macro index s "…" "save message to a mailbox"
-
…brings up the
save-message
folder selection menu, just after binding<Return>
to a macro that…<enter-command> macro browser <Return> "…"<Return><save-message>?
-
…selects a folder to save, syncs the mailbox, and finally restores
<Return>
to its original binding.<select-entry><sync-mailbox><enter-command> bind browser <Return> select-entry<Return>
This solves our problem, but creates a new one: if you go to save a message and then change your mind, you can exit the folder selection menu by pressing q
. Only now, the macro hasn’t had a chance to clean up after itself (as in Step 2 above); <Return>
is still mapped to a macro that will select-entry
, sync-mailbox
, and then restore <Return>
to its original binding.
We need more macros
To fix this, we have to incorporate yet another macro definition into our already very complicated meta-macro. We have to map both q
and <Return>
to their own separate “clean-up” macros:
macro index s "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<save-message>?" "save message to a mailbox"
Let’s break this down again.
-
Bind
s
to a macro that…macro index s "…" "save message to a mailbox"
-
…brings up the
save-message
folder selection menu, just after binding<Return>
andq
to two separate macros…<enter-command> macro browser <Return> "…"<Return><enter-command> macro browser q "…"<Return><save-message>?
-
…the first of which selects a folder to save, syncs the mailbox, and finally restores
<Return>
andq
to their original bindings…<select-entry><sync-mailbox><enter-command> bind browser <Return> select-entry<Return><enter-command> bind browser q exit<Return>
-
…while the other exits and cleans up likewise.
<exit><enter-command> bind browser <Return> select-entry<Return><enter-command> bind browser q exit<Return>
The Final Configuration
I have my own muttrc
set up to be as ‘vimmy’ as possible, so the macros described above are actually bound to dd
and ss
, with dat
and sat
set up to “delete a thread” and “save a thread,” respectively.
Also worth noting is the inclusion of threadwise saving and copying. No such function is built into mutt, so these mappings automate tag-thread
+ save-message
(even in pager view, where tag-thread
is not available).
macro index,pager dd "<delete-message><sync-mailbox>" "move message to trash"
macro index,pager dat "<delete-thread><sync-mailbox>" "move thread to trash"
macro index,pager ss "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<save-message>?" "save message to a mailbox"
macro index sat "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015'q<untag-pattern>.\\015\"\015<mark-message>q<enter><untag-pattern>.<enter><tag-thread><tag-prefix-cond><save-message>?" "save thread to a mailbox"
macro index \;s "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<tag-prefix-cond><save-message>?" "save tagged messages to a mailbox"
macro pager sat "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015<display-message>\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015'q<untag-pattern>.\\015<display-message>\"\015<exit><mark-message>q<enter><untag-pattern>.<enter><tag-thread><tag-prefix><save-message>?" "save thread to a mailbox"
macro index,pager cc "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<copy-message>?" "copy message to a mailbox"
macro index cat "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015'q<untag-pattern>.\\015\"\015<mark-message>q<enter><untag-pattern>.<enter><tag-thread><tag-prefix-cond><copy-message>?" "copy thread to a mailbox"
macro index \;c "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015\"\015<tag-prefix-cond><copy-message>?" "copy tagged messages to a mailbox"
macro pager cat "<enter-command> macro browser \\015 \"\<select-entry\>\<sync-mailbox\><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015<display-message>\"\015<enter-command> macro browser q \"<exit><enter-command> bind browser \\\\015 select-entry\\015<enter-command> bind browser q exit\\015'q<untag-pattern>.\\015<display-message>\"\015<exit><mark-message>q<enter><untag-pattern>.<enter><tag-thread><tag-prefix><copy-message>?" "copy thread to a mailbox"
And this is why sometimes I wish I had a different hobby.