Vimの折畳に対応したtreeコマンド
golang.tokyo #21のDevQuizはGoでtreeコマンドを作成するというものでした。
treeコマンドといえば、以前Vimでファイル一覧をツリー表示するためのtree.vimというプラグインを作る時に使いました。
このプラグインはディレクトリにマーカーをつけることでサブディレクトリを階層ごとに折り畳んで表示します。
当時、マーカーを(Go製の)自作コマンドでつけることも検討しましたが、実装が非常に面倒くさそうだったのでVim scriptでtreeコマンドの結果をパースすることにしました。
そんな事情があったため、せっかくなのでDevQuizをやりつつ、追加機能としてVimの折畳に対応した出力形式も実装してしまおう!と思って作ったのがgo-treeです。*1
こちらはコマンドラインフラグに-V
をつけると以下のような出力になります。
$ go-tree -V github.com/daisuzu/tree.vim github.com/daisuzu/tree.vim/../ github.com/daisuzu/tree.vim/./ github.com/daisuzu/tree.vim/README.md github.com/daisuzu/tree.vim/autoload/{{{ github.com/daisuzu/tree.vim/autoload/tree.vim}}} github.com/daisuzu/tree.vim/ftplugin/{{{ github.com/daisuzu/tree.vim/ftplugin/tree.vim}}} github.com/daisuzu/tree.vim/plugin/{{{ github.com/daisuzu/tree.vim/plugin/tree.vim}}} github.com/daisuzu/tree.vim/syntax/{{{ github.com/daisuzu/tree.vim/syntax/tree.vim}}}
コマンド自体はVimから実行しても良いし、
:r! go-tree -V [DIR]
シェルで実行した結果をVimに流し込んでもOKです。
# -cでfoldmethod(fdm)をmarkerにする go-tree -V [DIR] | vim - -R -c 'setl fdm=marker' # モードラインでfoldmethod(fdm)をmarkerにする echo -e "vim: fdm=marker\n$(go-tree -V [DIR])" | vim - -R
そうするとgithub.com/daisuzu/tree.vim
はVimで以下のように表示されます。
github.com/daisuzu/tree.vim/../ github.com/daisuzu/tree.vim/./ github.com/daisuzu/tree.vim/README.md +-- 2 lines: github.com/daisuzu/tree.vim/autoload/----------------------------- +-- 2 lines: github.com/daisuzu/tree.vim/ftplugin/----------------------------- +-- 2 lines: github.com/daisuzu/tree.vim/plugin/------------------------------- +-- 2 lines: github.com/daisuzu/tree.vim/syntax/------------------------------- ~ ~ ~ ~
さらに、コマンド単体で動作するのでjob機能を使った非同期実行も簡単です。
:vnew | setl fdm=marker | call job_start('go-tree -V', {'out_io': 'buffer', 'out_buf': bufnr('%')})
このように、github.com
配下に大量のファイルがあるような場合でもVimをブロックすることなく操作を継続することができます。
現在はvimrcにAtree
コマンドを定義してgo-treeを常用しています。
filetypeをtree.vim
のものにすることで、そのまま表示するよりも少し見栄えが良くなっています。
command! -nargs=? -complete=dir -count -bang -bar ATree \ call s:async_tree(<q-args>, <count>, <bang>0, <q-mods>) function! s:async_tree(dir, depth, bang, mods) abort let cmd = 'go-tree -V ' . g:tree_options if a:bang " コマンドに!をつけたら隠しファイルも表示する let cmd .= ' -a' endif if a:depth > 0 let cmd .= ' -L ' . a:depth endif " cd後もgfできるようにディレクトリ名を絶対パスにする let cmd .= ' ' . fnamemodify(a:dir != '' ? a:dir : '.', ':p:h') execute a:mods . ' new' setfiletype tree let bufnr = bufnr('%') call job_start(cmd, { \ 'out_io': 'buffer', \ 'out_buf': bufnr, \ 'exit_cb': {channel, msg -> s:goto_first(bufnr)}, \ }) endfunction " オリジナル版はカーソル位置が先頭行になるので同じ挙動にする function! s:goto_first(bufnr) if bufnr('%') != a:bufnr " バッファを移動していたら何もしない return endif if line('.') != line('$') " 行を移動していたら何もしない return endif normal gg endfunction
予想通り実装は面倒でしたが、Vimをより快適に使えるようになりました!
*1:人が見ることはあまり考えてませんでした...