軽量ディレクトリビューワ dirvish をカスタマイズする方法

# Vim のファイラ選び

Vim には幾らかファイラがあります。

* Netrw (Vim標準)
* [NERDTree](https://github.com/scrooloose/nerdtree)
* [Vaffle](http://cocopon.me/blog/?p=6084)
* [Vimfiler](https://github.com/Shougo/vimfiler.vim)

他にもいろいろあります。僕も昔は NERDTree を使っていたのですが、軽さを求めていたので [dirvish](https://github.com/justinmk/vim-dirvish) にスイッチしました。

[Big Sky :: Vim 上のファイラを NERDTree から dirvish に乗り換えた。](https://mattn.kaoriya.net/software/vim/20150310202155.htm)

dirvish はとにかく何もしません。何もしないので機能が無いのですが、その分速いという訳です。README.md によると Netrw の2倍速いとのこと。

ただ、全てを気に入っているかというと、そういう訳ではなくて、幾らか不満もあります。

以下は僕がどうしても気に入らなかった2つの問題を解決する方法を紹介します。

# `-` で起動してしまう

デフォルトではノーマルモードの `-` で dirvish が起動します。オペレータ等では反応しないので問題ないのですが、たまにキータイプをミスした時に画面を dirvish にすり替えられるのが許せませんでした。

ただしこの挙動は既にマッピングがあれば登録されない様になっているので以下を vimrc 入れておくことで回避出来ます。

```vim
nmap <Plug>(nomap-dirvish_up) <Plug>(dirvish_up)
```

# 検索プロンプトが独自

dirvish は独自で検索プロンプト `/` をハックしており、ファイル名のみマッチする様に作り込まれています。それはそれで便利なのですが、これの実装がムズムズする実装なのです。`/` をタイプすると

```
/\ze[^\/]*[\/]\=$
```

といった様に `\ze[^\/]*[\/]\=$` という文字を強制で入れられてしまいます。要は `/` を含まない後続部分にマッチしている訳ですが、こんなのが後ろに付いた状態で文字を入力したくない訳です。

運よく dirvish のバッファは `dirvish` というファイルタイプを設定してくれているので、カスタマイズする事が出来ます。

```vim
augroup DirVish
  au!
  autocmd FileType dirvish (何か)
augroup END
```

ここでキーを無効にする為に以下の様に書いても良いかもしれません。

```vim
function! s:setup_dirvish()
  nunmap <buffer> /
  nunmap <buffer> ?
endfunction

augroup DirVish
  au!
  autocmd FileType dirvish :call s:setup_dirvish()
augroup END
```

もし「絶対にファイル名だけにマッチして欲しいけど、あの挙動だけは許せない」という人が居れば [vim-prompter](https://github.com/mattn/vim-prompter) を使う事も出来ます。vim-prompter は Vim のコマンドラインを疑似するプラグインで、独自の補完やフックを行える様になっています。

```vim
function! s:on_enter(input)
  echo "YOU MAN!: " . join(a:input, '')
endfunction

function! s:on_cancel(input)
  echo "WHY JAPANESE PEOPLE!"
endfunction

call prompter#input({
\ 'prompt': '# ',
\ 'on_enter':  function('s:on_enter'),
\ 'on_cancel': function('s:on_cancel'),
\})
```

この例を実行すると確定時に `YOU WIN!` を、キャンセル時に `WHY JAPANESE PEOPLE!` を表示します。

```vim
function! s:on_change(input)
  call clearmatches()
  exe "match Search /" . join(a:input, '') . "/"
endfunction

function! s:on_enter(input)
  call clearmatches()
  echohl Warning | echo "WHY JAPANESE PEOPLE!: " . join(a:input, '') | echohl None
endfunction

function! s:complete(input)
  return split(glob(a:input[0] . '*'), "\n")
endfunction

call prompter#input({
\ 'color': 'Normal',
\ 'prompt': '/',
\ 'on_complete': function('s:complete'),
\ 'on_enter':  function('s:on_enter'),
\ 'on_change':  function('s:on_change'),
\})
```
`on_complete` イベントや、`on_change` イベントをフックする事でインクリメンタルサーチを実現する事も出来ます。

これを使って dirvish の `/` を置き換えてしまった物が以下になります。

```vim
nmap <Plug>(nomap-dirvish_up) <Plug>(dirvish_up)

augroup DirVish
  au!
  autocmd FileType dirvish :call s:setup_dirvish()
augroup END

function! s:setup_dirvish()
  nunmap <buffer> /
  nunmap <buffer> ?

  call prompter#ready()
  nnoremap <buffer> / :<c-u>call <SID>search('/')<CR>
  nnoremap <buffer> ? :<c-u>call <SID>search('?')<CR>
endfunction

function! s:search(dir)
  let s:dir = a:dir
  let s:pos = getpos('.')
  call prompter#input({
  \ 'prompt': a:dir,
  \ 'prompt_color': 'Normal',
  \ 'cursor': '_',
  \ 'cursor_color': 'StatusLine',
  \ 'histtype': '/',
  \ 'on_change': function('<SID>on_change'),
  \ 'on_enter': function('<SID>on_enter'),
  \ 'on_cancel': function('<SID>on_cancel'),
  \ })
endfunction

function! s:on_cancel(input)
  call setpos('.', s:pos)
endfunction

function! s:on_enter(input)
  let @/ = join(a:input, '') . '\ze[^\/]*[\/]\=$'
endfunction

function! s:on_change(input)
  call setpos('.', s:pos)
  call search(join(a:input, '') . '\ze[^\/]*[\/]\=$', s:dir == '/' ? '' : 'bw')
  normal! 0
endfunction
```

あくまで疑似なので通常のプロンプトの動作とは異なります。これも嫌だという片は、おとなしく `nunmap` したバージョンをお使い下さい。