markhuge

Background fetch (or any command) in mutt

Published by on

By default, mutt runs external commands in the foreground of your mutt window. For long running tasks this can get kind of annoying.

I wrote a quick shell script to detect whether or not I’m in a tmux session, and if so, spawn an auto-closing background window to run offlineimap:

#!/usr/bin/sh

# Are we in tmux?
if [ -z "${TMUX}" ]; then

  # Did we pass in an account? 
  if [ -z "${1}" ]; then
    offlineimap
  else 
    offlineimap -a "$1"
  fi

else
  # Did we pass in an account? 
  if [ -z "${1}" ]; then
    j
    ####################################################
    # The -d flag tells tmux to create the new window,
    # but not switch focus to it
    ####################################################

    tmux new-window -d -n "fetching mail..." offlineimap 
  else
    tmux new-window -d -n "fetching mail..." offlineimap -a  "$1"
  fi

fi

Then I updated the macro in my muttrc to make use of the new handler:

macro index 0 "<enter-command>unset wait_key<enter><shell-escape>/path/to/syncmail.sh myaccountid<enter>" "run offlineimap to sync all mail"
  • macro index 0 triggers whenever the 0 key is pressed in the mutt index view. There’s nothing special about this key or this view. You could bind it to any key in any view, this is just how mine is set up.
  • <enter-command>unset wait_key<enter> prevents having to press enter to return to the mutt index after kicking off the background sync.
  • <shell-escape> runs the script

Obviously this pattern isn’t limited to mail sync. You could use this to run any background task from within mutt, or from any other application running within tmux.

I considered making a generic tmux backgrounder script that would exec whatever args. This is definitely more composable, but I’ll cross that bridge if I ever have another use case.

Notes

There’s some repetition in the script, but it seems like that’s unavoidable. I’d preffer to encapsulate the offlineimap argument check in a function, but there doesn’t seem to be a way to have tmux evaluate shell functions.

I use multiple email accounts with offlineimap and opt to only sync the current one. If you only have one (or always want to sync all accounts), you can simplify the script to:

#!/usr/bin/sh

# Are we in tmux?
if [ -z "${TMUX}" ]; then
    offlineimap
else
    tmux new-window -d -n "fetching mail..." offlineimap 
fi

and the macro to:

macro index 0 "<enter-command>unset wait_key<enter><shell-escape>/path/to/syncmail.sh<enter>" "run offlineimap to sync all mail"