I'm trying to write a wrapper command around :edit
or :find
specifically to open either my vimrc or a subpath of ~/.vim/
(e.g. ~.vim/autoloads/foo.vim
).
If I run the command with no arguments, it should open ~/.vim/vimrc
. It should accept an argument to specify a subpath of ~/.vim/
, and provide tab completion of those subpaths.
I have something that works, but it feels overly complicated. I'm having to jump through hoops to tweak 'path'
before Vim's command completion kicks in.
let s:vimrc = resolve($MYVIMRC)
let s:vimrc_dir = fnamemodify(s:vimrc, ':h')
nnoremap <leader>f<CR> :call PreparePath()<CR>:EditDotVim
command! -complete=file_in_path -nargs=? EditDotVim :
\ call EditDotVim(<f-args>) |
\ call RestorePath()
func! PreparePath()
let s:orig_path = &l:path
let s:orig_buf = bufnr('')
exec 'setl path='.s:vimrc_dir
endf
func! EditDotVim(...)
execute 'edit' a:0 ? a:1 : s:vimrc
endf
func! RestorePath()
call setbufvar(s:orig_buf, '&path', s:orig_path)
unlet s:orig_path
unlet s:orig_buf
endf
I was originally using a custom completion function and using the find
shell builtin to provide completions, but given Vim's 'path'
variable and :find
command, it seemed like I was reinventing the wheel.
Is there a more straightforward way to do this?
UPDATE
I'm still curious if there's a better way, but for now I've reverted to my original strategy, using custom completion and a little shell magic. I wouldn't call it slow, but it's not quite as snappy as I'd like, and I don't know how portable the find
regex is. But it works for now. I moved this into my autoload directory:
let s:vimrc = resolve($MYVIMRC)
let s:vimrc_dir = fnamemodify(s:vimrc, ':h')
let s:sub_paths = [
\ 'after',
\ 'autoload',
\ 'colors',
\ 'compiler',
\ 'ftdetect',
\ 'ftplugin',
\ 'indent',
\ 'init',
\ 'plugged',
\ 'syntax',
\ 'tmp',
\ 'todo.org',
\ 'view',
\ 'vimrc'
\]
let s:find_paths = '{'.join(s:sub_paths, ',').'}'
func! dotvim#edit(...)
execute 'edit' (a:0 ? dotvim#path(a:1) : s:vimrc)
endf
func! dotvim#list(ArgLead, CmdLine, CursorPos)
let findcmd = "find " . s:find_paths . " -regex '".a:ArgLead."[^/]*' -exec ls -pd {} +"
return system("cd " . s:vimrc_dir . " && " . findcmd)
endf
func! dotvim#path(subpath)
return s:vimrc_dir . '/' . a:subpath
endf
I went back and forth on the hard-coded paths whitelist vs calling out to ls
to generate it, vs using find *
(if I do find .
I get pesky leading ./
in my results), but ultimately the explicit whitelist makes it easy for me to tune which paths I actually care about.
Oh, and please ignore the todo.org
file in there. I know it's a cardinal sin having even trace amounts of emacs in my .vim/
...
UPDATE II
Incorporating the globpath
suggestion, this got a lot cleaner:
func! dotvim#list(ArgLead, CmdLine, CursorPos)
return map(globpath(s:vimrc_dir, a:ArgLead.'*', 0, 1), s:subpath_map)
endf
let s:subpath_map = { k,v -> fnamemodify(v,':p')[(s:leading_len):] }
let s:leading_len = len(s:vimrc_dir) + 1
command! -complete=customlist,dotvim#list -nargs=? DotVim call dotvim#edit(<f-args>)