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!