teaching machines

Automatic Alias Creation in Mutt/Vim

A friend has got me thinking about using Mutt again. I gave it up years ago after I was forced to use IMAP for one of my mail accounts, and I didn’t think Mutt was up to the task. I switched to Thunderbird, which lets me do certain things faster. However, I’ve really missed a few features from Mutt: textual and portable configuration files, multiple message replies, and full Vim support.

As I return to Mutt, I also miss a few things from Thunderbird. The most prominent of these is autocompletion of my recipients. Mutt will tab-complete aliases that you’ve previously registered in its configuration files, but Thunderbird one-ups Mutt by automatically storing the addresses of people you send messages to. I wanted to make Mutt do that too.

I looked around for hooks, but I didn’t see a way for Mutt to automatically record aliases when I sent a message. However, since I was using Vim to edit my messages, I saw that I could create an autocommand that would strip out the recipients’ addresses from my message and append them to my alias file. So, I first wrote a function to do the heavy-lifting:

" This function, which is to be run only on a Mutt email message,
" finds all the addresses in the To, Cc, and Bcc headers. If
" no aliases exist for these addresses, they are added to the
" alias file.
function! AddMuttAliases()

  " This is where my alias file lives. Change this to something
  " that suits you.
  let aliasfile = $UU . '/configs/mutt/aliases'

  " Why isn't there a match + matchstr? match only gives me the
  " start, which is not enough to extract the substring.
  " matchstr doesn't give me the location, which is not enough
  " information to advance forward.

  " Find all email addresses. My pattern may not be the best yet.
  let addresses = []
  silent vimgrep /^\(To\|Cc\|Bcc\):/j %
  for line in getqflist()
    let pattern = '[-A-z._]\+@[-A-z._]\+'
    let start = match(line.text, pattern)
    let address = matchstr(line.text, pattern, start)
    while address != ""
      call add(addresses, address)
      let start += len(address)
      let address = matchstr(line.text, pattern, start)
    endwhile
  endfor

  " Check to see if each is in aliases file. If not, add it.
  " Mutt's alias registration command has this form:
  "
  "   alias name address
  "
  " Usually the name is something short and mnemonic. I prefer
  " to continually work with people's addresses so that I'm not
  " too crippled when I lose my configuration.  So, my aliases
  " have this form:
  "
  "   alias address address
  "
  " Isn't this overly verbose? Yes. What have I gained? Well,
  " mutt will tab-complete the addresses for me. Verbosity
  " doesn't matter. And that's sweet.
  for address in addresses
    execute 'silent! vimgrep /' . address . '/j ' . aliasfile
    if len(getqflist()) == 0
      let aliasline = 'alias ' . address . ' ' . address
      silent! execute '!echo ' . aliasline . ' >> ' . aliasfile
    endif
  endfor
endfunction

To call this function when Mutt messages are written, I registered the following autocommand in my .vimrc file:

autocmd BufWritePost ~/.tmp/mutt-* call AddMuttAliases()

This command assumes Mutt messages are temporarily stored in ~/.tmp, which I set in my .muttrc.

Now everytime I write a message to someone new, I get an alias added to my aliases file. The next time, I can simply tab-complete to enter the person’s address!

Comments

  1. Zak says:

    Thanks! I can get vim to execute that command from within mutt, but do you know how to have mutt add email addresses to messages when editing? The files I’m parsing just have the recipient’s names…

Leave a Reply

Your email address will not be published. Required fields are marked *