TITLE: Using nnn.vim as a filepicker for neomutt attachments
DATE: 2021-05-15
AUTHOR: John L. Godlee
====================================================================


In a previous post I talked about using vifm as a filepicker within
vim to choose files to attach to an email sent by the neomutt email
client.

 [vifm]: https://vifm.info/
 [neomutt]: https://neomutt.org/

I got an email from someone who had read that post, asking a few
questions to get the system running properly. I hadn't actually
used that workflow for some time, and when I tried to get it to run
I found that it didn't work anymore. After some light debugging I
realised that the problem was that I had switched to neovim, a fork
of vim which aims to improve on vim in some areas where it has
stagnated. One major difference between neovim and vim is that
neovim can't do interactive 'bang' commands. Instead everything has
to be done through the :terminal buffer.

 [neovim]: https://neovim.io/
 [neovim can't do interactive 'bang' commands]:
https://github.com/neovim/neovim/issues/1496

This problem led me down a rabbit hole to construct a better
attachment picker for neovim.

I don't actually use vifm anymore. I find that I rarely need its
split-panel capabilities. I generally use basic ls, mv, etc, and on
the occasion I need to move multiple files with regex or whatever,
I use vimv, a very simple bash script.

 [vimv]: https://github.com/thameera/vimv

I do keep a version of the nnn file manager on my system however,
just in case. nnn is fast and minimal enough in its un-patched out
of the box state that I never have to think about it. I found out
that there is a nnn.vim
plugin](https://github.com/mcchrish/nnn.vim) that works very
similar to the fzf.vim plugin that I already use for quickly
navigating files in vim. With some help from the maintainer of
nnn.vim ([ see this issue

 [nnn file manager]: https://github.com/jarun/nnn
 [fzf.vim plugin]: https://github.com/junegunn/fzf.vim

First the nnn.vim plugin must be installed, for example with
vim-plug: Plug 'mcchrish/nnn.vim'.

Then write a function to format the output of nnn.vim:

   function! s:mutt_attach(lines)
     let prettylines = ''
     for i in a:lines
       let prettylines .= 'Attach: ' . fnameescape(i) . "\n"
     endfor
     6put =prettylines
   endfunction

This function will get the filenames returned by nnn.vim, escape
any special characters in those filenames with fnameescape(i), wrap
the filenames with Attach: before the name and a newline after the
name, then paste all the lines at the 6th line of the vim buffer,
which is where the mutt headers are located by default.

Then, to call this function, it's necessary to write a custom
nnn#pick function that itself can handle an external function as
the edit command:

   function! s:nnncall(...)
     let l:dir = get(a:, 1, '')
     let l:opts = get(a:, 2, { 'edit': 'edit' })
     let l:keypress = get(a:, 3, '')
     call nnn#pick(l:dir, l:opts)
     if strlen(l:keypress) > 0
       call feedkeys(l:keypress)
     endif
   endfunction

This is the bit I got from @mcchrish.

And finally write a file type specific mapping that only works in
mail filetypes:

   autocmd Filetype mail nnoremap <silent> <Leader>A :call
<SID>nnncall('/Users/johngodlee', { 'edit':
function('<SID>mutt_attach') })<CR>

 ![nnn.vim in
action](https://johngodlee.xyz/img_full/nnn_mutt/nnn.png)

 ![Results pasted into vim
buffer](https://johngodlee.xyz/img_full/nnn_mutt/att.png)