Vim vimfilerのunexpand_treeを呼び出す

vimfilerのキーバインドを変更していて
lでツリーを開く
hでツリーを閉じる
というのをやろうとすると以下のようなコードになる。

```vim:vimrc
autocmd FileType vimfiler nmap <silent><buffer>l <Plug>(vimfiler_expand_tree)
autocmd FileType vimfiler nmap <silent><buffer>h <Plug>(vimfiler_unexpand_tree) 
```


しかし\<Plug>(vimfiler_unexpand_tree)は定義されていないのでエラーになる。
vimfilerをexplorerモードで起動したとき \<Plug>(vimfiler_smart_h) がツリーを閉じる動作をするが、explorerモード以外でもツリーを閉じる動作が使いたい。
この記事ではvimfilerのプラグインを改造することなく、\<Plug>(vimfiler_unexpand_tree)を使えるようにする。

この記事はvim初心者から中級者向けにそこそこ細かく解説する。
手っ取り早く最終的なコードだけ知りたい場合は一番下の方を見てほしい。

#ツリーを閉じる関数
vimfilerのコードの中にs:unexpand_tree()という関数がある。
これを呼び出せば問題は解決するが、スクリプトローカルになっているので外部から呼び出すことはできない。

と思っていたがどうやらスクリプトローカルな関数を外部から呼び出す方法があるらしい。
http://mattn.kaoriya.net/software/vim/20110728094347.htm
以下ようにすればスクリプトローカル関数でも外部から呼び出せるようだ。

```vim
:call <SNR>スクリプト番号_関数名()
```

> スクリプト番号とはスクリプトファイル1つに割り当てられた数値

スクリプト番号を取得するにはいくつかの方法があるが、今回は手っ取り早く <b>呼び出すべき関数名</b> をまとめて取得する。


#関数名を取得する
パターンにマッチする関数名を取得するには以下のようにする。

```vim
:function /{pattern}
```

今回はunexpand_treeを取得したいので

```vim
:function /\d+*_unexpand_tree\(\)$
```

とすると以下のような出力が得られる。(数値は環境によって異なる)

```
function <SNR>268_unexpand_tree()
```

※vimfiler以外でs:unexpand_treeが定義されている場合はこの方法ではうまく行かないかもしれない。


#コマンドの出力結果を変数に入れる
関数名が出力できたので、今度は \:redir を使って出力を変数にいれる。
https://gist.github.com/Shougo/369112
を参考にして以下のようなコマンドを作った。

```vim
" 引数のコマンドの出力を g:captureに入れる
command! -nargs=+ -bang -complete=command
      \ Capture call Capture(<q-args>)

function! Capture(cmd)
  redir => l:out
  silent execute a:cmd
  redir END
  let g:capture = substitute(l:out, '\%^\n', '', '')
  return g:capture
endfunction
```

```vim
:Capture ls
:echo g:capture
```

のように使う。

これを使って関数名を変数に入れるには以下のようにする。

```vim
:Capture function /\d+*_unexpand_tree\(\)$
```

これで変数 g:capture に関数名が入る。

#\<Plug>(vimfiler_unexpand_tree)を定義する
ここまでのコードで g:captureには以下のような文字列が入っていると思う。

```vim
function <SNR>268_unexpand_tree()
```

あとはこの文字列を使って \<Plug>(vimfiler_unexpand_tree)を定義すればいい。

前処理としてg:captureに入ってる文字列から"function"という文字列を消しておく必要がある。

```vim
let func_name = substitute(g:capture, '^function ', '', '')
```


最後に\<Plug>(vimfiler_unexpand_tree)を定義するには \:executeを使う。
http://vim-jp.org/vimdoc-ja/eval.html#:execute

```vim
execute 'nnoremap <buffer><silent> <Plug>(vimfiler_unexpand_tree) :<C-u>call' func_name '<CR>'
```

これで\<Plug>(vimfiler_unexpand_tree)を呼び出せるようになる。

#最終結果

```vim
" コマンドの出力結果を g:captureに入れるコマンド
command! -nargs=+ -bang -complete=command
      \ Capture call Capture(<q-args>)

function! Capture(cmd)
  redir => l:out
  silent execute a:cmd
  redir END
  let g:capture = substitute(l:out, '\%^\n', '', '')
  return g:capture
endfunction

" <Plug>(vimfiler_unexpand_tree)を定義する関数
function! s:set_vimfiler_unexpand_tree()
  "すでに存在していれば終了
  if hasmapto("<Plug>(vimfiler_unexpand_tree)")
    return
  endif

  " 名前を取得
  Capture function /\d+*_unexpand_tree\(\)$
  let l:func_name = substitute(g:capture, '^function ', '', '')

  if empty(l:func_name)
    return
  endif

  " <Plug>(vimfiler_unexpand_tree)を定義
  execute 'nnoremap <buffer><silent> <Plug>(vimfiler_unexpand_tree) :<C-u>call' l:func_name '<CR>'
endfunction

" vimfilerに入ったとき設定を適用する
autocmd FileType vimfiler call s:vimfiler_setting()
function! s:vimfiler_setting()
  call s:set_vimfiler_unexpand_tree()

  nmap <silent><buffer>h <Plug>(vimfiler_unexpand_tree)
  nmap <silent><buffer>l <Plug>(vimfiler_expand_tree)
endfunction

```

#余談
vimfilerをexplorerモードで起動するとバッファ一覧に表示されない。
explorerモード以外でもバッファ一覧に表示されないようにするには、s:vimfiler_setting()の中に以下の一行を足せばいい。

```vim
setlocal nobuflisted
```