[dein.vim] hook の便利な使い方

dein.vim は NeoBundle に変わるプラグインマネージャです。詳しくはこちらの記事に書きました。

> [NeoBundle から dein.vim に乗り換えたら爆速だった話 - Qiita](http://qiita.com/delphinus35/items/00ff2c0ba972c6e41542)

プラグインのリストを TOML ファイルに書くとき、そこにプラグインの設定も一緒に入れてしまうといろいろ捗ります。

> [dein.vimによるプラグイン管理のマイベストプラクティス - Qiita](http://qiita.com/kawaz/items/ee725f6214f91337b42b)

この記事ではこれに使われる **hook 機能**についてまとめてみました。

# hook の種類

| hook の名前        | 実行されるタイミング                         | lazy OFF | lazy ON |
|--------------------|----------------------------------------------|:--------:|:-------:|
| `hook_add`         | プラグインが dein.vim によって追加されたとき |    OK    |    OK   |
| `hook_source`      | プラグインが読み込まれる直前                 |    NG    |    OK   |
| `hook_post_source` | プラグインが読み込まれた直後                 |    NG    |    OK   |
| `hook_post_update` | プラグインが更新された直後                   |    OK    |    OK   |
| `hook_done_update` | プラグイン全ての更新が終わった直後           |    OK    |    OK   |

以下、それぞれについて例を挙げて見ていきます。

## `hook_add`

dein.vim によってプラグインが認識されたときに発火される hook です。non lazy な（Vim の起動と同時に読み込まれる）プラグインの場合、設定情報を置くにはこれを使うしかありません。

便利な用途としては、プラグインを起動するための mapping を書いたり、プラグインのための変数を設定したりします。

```sh
[[plugins]]
repo     = 'tpope/vim-capslock'
hook_add = 'imap <C-L> <Plug>CapsLockToggle'

[[plugins]]
repo     = 'elzr/vim-json'
hook_add = 'let g:vim_json_syntax_conceal = 0'
```

## `hook_source`

プラグインを読み込む直前に実行されます。たとえばこんな感じ。

```sh
[[plugins]]
repo        = 'Shougo/echodoc.vim'
hook_source = 'let g:echodoc_enable_at_startup = 1'
```

この程度の分量なら `hook_add` と大して違いはない（あるいは `vimrc` に直接書くのと変わらない）ですが、本当に必要になるときまで設定を遅らせることができるので、Vim 起動時に環境を汚さないようになります。

## `hook_post_source`

プラグインを読み込んだ直後に実行されます。`hook_source` と似てますが、プラグイン自体の機能が必要なときはこちらに設定しなければなりません。

```sh
[[plugins]]
repo    = 'osyo-manga/vim-watchdogs'
depends = [
  'vim-hier',
  'quickfixstatus',
  'shabadou.vim',
  'vim-quickrun',
]
# 追記参照 2016/05/09
on_event = ['FocusLost', 'CursorHold']
hook_add = '''
  let g:quickrun_config = get(g:, 'quickrun_config', {})
  let g:quickrun_config['watchdogs_checker/_'] = {'outputter/quickfix/open_cmd': ''}
  let g:quickrun_config['watchdogs_checker/jshint'] = {'cmdopt': '--config `/.jshintrc'}
'''
hook_post_source = '''
  call watchdogs#setup(g:quickrun_config)
'''
```

[vim-watchdogs][] はファイル保存時などに非同期でコンパイルチェックをしてくれるプラグインです。依存関係の複雑なプラグインですので、`on_event` を使って Vim 起動後、非同期に読み込んでいます。

[vim-watchdogs]: https://github.com/osyo-manga/vim-watchdogs

/* 追記ここから 2016/05/09 */

`on_idle` は deprecated になりました。[該当部分のコード][on_idle]を見るとわかりますように、現在は単なる `on_event` へのエイリアスになっています。

[on_idle]: https://github.com/Shougo/dein.vim/blob/c441cf8c22521ab220917e88c14dade56bf82859/autoload/dein/parse.vim#L155-L157

/* 追記ここまで */

このプラグインは設定の最後に `watchdogs#setup()` を呼ぶ必要があるのですが、これは当然、プラグインを読み込んだ後でないと実行できません。こういうときに `hook_post_source` は便利です。

## `hook_post_update`

[vimproc.vim][] や [Tern for Vim][] 等、インストール後にコンパイルや関連ツールのインストールが必要なプラグインがあります。dein.vim では、このようなとき `build` オプションが使えます。

[vimproc.vim]: https://github.com/Shougo/vimproc.vim
[Tern for Vim]: https://github.com/ternjs/tern_for_vim

```sh
[[plugins]]
repo  = 'Shougo/vimproc.vim'
build = 'make'
```

以前までこのオプションには OS 毎に別のコマンドを指定することが出来ましたがその機能がなくなってしまいました（[このコメント][]で教えていただきました。thx!）。現在はこのような用途で `hook_post_update` を使うことが[推奨されています][]。

[このコメント]: http://qiita.com/delphinus35/items/00ff2c0ba972c6e41542#comment-97471a8d81044e70346a
[推奨されています]: https://twitter.com/ShougoMatsu/status/716111235918331904

```sh
[[plugins]]
repo = 'Shougo/vimproc.vim'
hook_post_update = '''
  if has('win32')
    let cmd = 'tools\\update-dll-mingw'
  elseif has('win32unix') " for Cygwin
    let cmd = 'make -f make_cygwin.mak'
  elseif executable('gmake')
    let cmd = 'gmake'
  else
    let cmd = 'make'
  endif
  let g:dein#plugin.build = cmd
'''
```

別にこの `hook_post_update` に限った話ではないのですが、全ての `hook` では `g:dein#plugin` を直接指定して設定を動的に書き換えることができます。[^1]

## `hook_done_update`

最後はこれです。`hook_post_update` は各プラグインのインストール直後に実行されるものですが、`hook_done_update` はプラグイン全てがインストールされた後（正確には、`Runtimepath updated: ...` のメッセージが表示された後）に実行されます。`call dein#update()` の後に、自動的に設定を読み込み直す用途などに使えそうです。

# 設定が膨らんできたら……

便利な `hook` なのですが、ここに設定をたくさん載せていくと今度は TOML がカオスになってしまいます。これを避けるため、設定が複雑になってきたら関数に切り出すようにしました。[^2]

```vim:~/.vim/vimrc
set runtimepath+=~/.vim
```

```vim:~/.vim/autoload/delphinus/init/vimproc.vim
function! delphinus#init#vimproc#hook_post_update() abort
  if has('win32')
    let cmd = 'tools\\update-dll-mingw'
  elseif has('win32unix') " for Cygwin
    let cmd = 'make -f make_cygwin.mak'
  elseif executable('gmake')
    let cmd = 'gmake'
  else
    let cmd = 'make'
  endif
  let g:dein#plugin.build = cmd
endfunction
```

```sh:dein.toml
[[plugins]]
repo = 'Shougo/vimproc.vim'
hook_post_update = 'call delphinus#init#vimproc#hook_post_update()'
```

`autoload` 関数にすると、本当に必要になったときだけ、ファイルが読み込まれるようになるので CPU にもストレージにも優しいです。このようにすれば、見通しよくプラグインの管理ができると思います。

[^1]: これできたらいいなー、と思って、[ここで要望したら][youbou]爆速で実行していただけました！！
[^2]: `autoload` 関数はあらゆるプラグインで使われています。関数の名前が被るとまずいので、プラグインに使われそうもない名前空間（ここでは僕のハンドル `delphinus`）に置かないといけません。

[youbou]: https://github.com/Shougo/dein.vim/issues/70