Mutt has it's strong sides and also weak sides. One of the weak ones is the file browser, that isn't really designed for actual browsing, as it
only displays one level of the file hierarchy at a time. If not entering a path directly, actual browsing is done by:
- either typing a subpath, then using the browser again on the partial path
- or one can tell mutt up front about the folders in existance using the mailboxes command (and also automatically populate that, as shown
here and here),
and then use the Mailboxes-view of the browser, which is a flat list
The problem is that both ways are frustrating to use with an ever growing mailbox.
The former makes it hard to see which folders and subfolders have unread mail and to get some overview over the hierarchy in general. The latter is a flat list, which
is hard to find anything in if you have a lot of nodes. There is a patchset for mutt adding a sidebar for browsing, but suffers from the same problem.
Some mail setups don't face those problems as they aren't file-system heavy, or use indexers. However, I'm used to MH as a mailstore, and like to drop/filter my mail
into many folders and subfolders, the old-school way. So, I decided to come up with some own solution, hooking up vim as a filebrowser. Note, the scripts below work with MH, only,
and require nmh to be accessible.
Add this "pretty" macro to your .muttrc. What it does is running nmh's flists command, uses the output to build a dummy directory tree under /tmp with number of unread
emails (summed up towards the root), then lets vim do the folder browsing. Once a folder is picked, it hands it back to mutt:
set wait_key=no
macro index,pager b '\
<shell-escape>\
if [ -d /tmp/mailbox_tree_mirror ]; then rm -r /tmp/mailbox_tree_mirror; fi;\
flists -alpha -recurse |\
sed -E "s@^(.*) has *([0-9]+) .*@\1/\2@" |\
sed -E "s@[+ ]*(/[0-9]+)\$@\1@" |\
awk "\
BEGIN{FS=\"/\"}\
{s=\"\";for(i=1;i<NF;++i){s=s\"/\"\$i;u[s]+=\$NF}}\
END{for(i in u){split(i,d);s=p=\"\";for(j in d){if(d[j]){s=s\"/\"d[j];p=p\"/\"d[j]\" (\"u[s]\")\"}}print p}}" |\
while read l; do mkdir -p "/tmp/mailbox_tree_mirror/$l"; done;\
(cd /tmp/mailbox_tree_mirror/ && vim -S ~/.mutt/browse_files.vim .);\
<enter>\
<enter-command>\
source /tmp/mailbox_tree_mirror/pick\
<enter>'
The macro is bound to b, and makes some assumptions about some paths. Adapt as needed.
Starting vim sources a file I put in ~/.muttrc called browse_files.vim, which
works with either netrw (usually comes with vim) or NERDTree:
Usage:
fu! SwitchToMailboxInMutt()
:silent !print push \'<change-folder>`span class="Statement">pwd|sed 's@^/.*tmp/mailbox_tree_mirror/@=@'|sed -E 's@ \([0-9]+\)(/|$)@/@g'|sed -E 's@( )@\<quote-char\>\1@'g`'\<return\> > /tmp/mailbox_tree_mirror/pick
:q!
endf
function! AddMuttSyntaxHL(parent)
exec 'syn match muttUnreadSel # ([0-9]\+)/# containedin='.a:parent.'Dir'
syn match muttUnreadCnt #([0-9]\+)# containedin=muttUnreadSel
syn match muttUnread0 #(0)# containedin=muttUnreadCnt
hi link muttUnreadSel Ignore
hi link muttUnreadCnt Number
hi link muttUnread0 Comment
endf
function! ApplyMuttNERDTreeBindings()
let g:NERDTreeMinimalUI=1
call AddMuttSyntaxHL('NERDTree')
:normal F
call cursor(line('.')+4)
map gm cd<CR>:call SwitchToMailboxInMutt()<CR>
endf
function! ApplyMuttNetrwBindings()
autocmd FileType netrw call AddMuttSyntaxHL('netrw')
let g:netrw_liststyle=3
let g:netrw_list_hide='.*[^/]$'
map gm <CR>:let s=b:netrw_curdir<CR><CR>:if(strlen(b:netrw_curdir)>strlen(s))\|let s=b:netrw_curdir\|endif<CR>:exec 'cd '.fnameescape(s)<CR>:call SwitchToMailboxInMutt()<CR>
endf
if &ft ==# "nerdtree"
call ApplyMuttNERDTreeBindings()
else
call ApplyMuttNetrwBindings()
endif
That's it - instead of having to deal with mutt's browser, I now hit b, browse and when I found the folder I want to browse to, hit gm in vim (goto mailbox), and
I'm back in mutt. Also, I get a quick overview of all my folders and the unread emails, even in color!