NeoBundle から dein.vim に乗り換えたら爆速だった話

たまたま NeoBundle のリポジトリを見たら[こんな PR][PR] が上がっておりました。平たく言うと、**NeoBundle はオワコンだから dein 使おう！**と作者の Shougo 氏自身が [reddit で発言した][reddit]そうなのです。僕自身は NeoBundle に何の不満もなかったのですが、**dein の方がとにかく爆速らしい**ということは各所で強調されてましたので試してみました。

[PR]: https://github.com/Shougo/neobundle.vim/pull/500
[reddit]: https://www.reddit.com/r/vim/comments/47qbv9/neobundle_on_hiatus_or_done/d0fcdel

# どのくらい速くなった？

最初に結論から。確かに起動速度は爆速です。インストール後、以下のコマンドを叩いてざっくり計測してみました。

```sh
vim --startuptime /tmp/time.log +q
```

* ↑のコマンドを 5 回叩いて平均を見ました。
* 利用した設定ファイルはココ。
  - https://github.com/delphinus35/dotfiles/tree/master/.vim
* MacVim Kaoriya 版 (7.4.1362)
* プラグイン総数: 98
  - そのうち、起動時に有効 (not lazy loading) なもの: 35
  * プラグインリストは全て TOML ファイルに書いています。
* その他諸々の設定含みます。

 | NeoBundle | dein.vim | + load_state()
----|---:|---:|---:
最長 | 179ms | 119ms |
最短 | 165ms | 114ms |
平均 | 170ms | 116ms | 85ms

> // 追記ここから 2016/03/22
>
> 以前設定ファイルの例として載せておりました `dein#load_cache()` を使った例は **deprecated** になりました。代替の `dein#load_state()` を使うと更に爆速に！NeoBundle から見れば半分の時間になってますね！驚きです。
>
> // 追記ここまで

**30% 以上速くなってます！**しかもこれ、他の諸々の設定込みですから人によってはもっと変わってくるでしょう。

速さはもちろんですが、設定項目が整理され、より分かりやすく、細かいところに手が届くようになってます。

で、なんだか順序が逆ですが、以下インストール方法と、NeoBundle から乗り換えるときの Tips を書いていきます。

# インストール方法

基本的なところは [dein.vimを使ってみる - Qiita][qiita] に既に上がってますので、ここでは [Shougo 氏の `vimrc`][vimrc] を見てよりスマートに（？）設定してみます。特に、プラグインリストの TOML 化は高速起動のために必須です。

[qiita]: http://qiita.com/yoza/items/2f8bd33a18225754f346
[vimrc]: https://github.com/Shougo/shougo-s-github/blob/master/vim/rc/vimrc

> // 追記ここから 2016/03/22
>
> 上にも書いてますが、`dein#load_cache()` は **deprecated** になったので、`dein#load_state()` を使った例に書き換えました。
>
> // 追記ここまで

```vim
" vimrc に以下のように追記

" プラグインが実際にインストールされるディレクトリ
let s:dein_dir = expand('~/.cache/dein')
" dein.vim 本体
let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim'

" dein.vim がなければ github から落としてくる
if &runtimepath !~# '/dein.vim'
  if !isdirectory(s:dein_repo_dir)
    execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
  endif
  execute 'set runtimepath^=' . fnamemodify(s:dein_repo_dir, ':p')
endif

" 設定開始
if dein#load_state(s:dein_dir)
  call dein#begin(s:dein_dir)

  " プラグインリストを収めた TOML ファイル
  " 予め TOML ファイル（後述）を用意しておく
  let g:rc_dir    = expand('~/.vim/rc')
  let s:toml      = g:rc_dir . '/dein.toml'
  let s:lazy_toml = g:rc_dir . '/dein_lazy.toml'

  " TOML を読み込み、キャッシュしておく
  call dein#load_toml(s:toml,      {'lazy': 0})
  call dein#load_toml(s:lazy_toml, {'lazy': 1})

  " 設定終了
  call dein#end()
  call dein#save_state()
endif

" もし、未インストールものものがあったらインストール
if dein#check_install()
  call dein#install()
endif
```

## プラグインのリストを書く

インストールするプラグインのリストは TOML に書いていきます。

1. 起動時に必ず読み込むもの
  - `~/.vim/rc/dein.toml`
2. 特定の条件で後から読み込むもの
  - `~/.vim/rc/dein_lazy.toml`

### 1. `dein.toml` の例

NeoBundle & dein.vim を初めて触る方は 1. だけで十分です。

> // 追記ここから 2016/04/09
>
> `build` パラメータの書式が変わりましたので `vimproc` の設定方法を修正しました。くわしくは[こちら][dein-hook]を参照してください。
>
> // 追記ここまで

[dein-hook]: http://qiita.com/delphinus35/items/cd221a450fd23506e81a

```sh
# 基本は github.com のレポジトリーを指定するだけ
[[plugins]]
repo = 'Shougo/dein.vim'

# git clone 後、実行すべきコマンドがある場合はこんな感じ
[[plugins]]
repo = 'Shougo/vimproc.vim'
hook_post_update = '''
  if dein#util#_is_windows()
    let cmd = 'tools\\update-dll-mingw'
  elseif dein#util#_is_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
'''

# ブランチやタグを指定したいとき
[[plugins]]
repo = 'delphinus35/typescript-vim'
rev  = 'colorize-template-strings'

# 特定の条件で読み込みたいとき
[[plugins]]
repo = 'elzr/vim-json'
if   = '''! has('kaoriya')'''

# 依存関係を指定したいとき
[[plugins]]
repo    = 'vim-airline/vim-airline'
depends = ['vim-airline-themes']

# 依存関係を指定したからと言って、自動でインストールはされない（ここは NeoBundle と違う）
[[plugins]]
repo = 'vim-airline/vim-airline-themes'
```

`if` とか NeoBundle には有りませんでしたね！超便利です。

### 2. `dein_lazy.toml` の例

プラグインの数が多くなってくると起動速度が遅くなってきます。必ずしも起動時に必要でないプラグインは、利用条件と共に 2. に移しましょう。

```sh
# 特定のファイルタイプで読み込む
[[plugins]]
repo  = 'Quramy/tsuquyomi'
on_ft = ['typescript']

# インサートモードに入ったら読み込む
[[plugins]]
repo = 'Shougo/neocomplete.vim'
on_i = 1

[[plugins]]
repo   = 'Shougo/unite.vim'

# unite.vim を読み込んだら一緒に読み込む
[[plugins]]
repo      = 'Shougo/neomru.vim'
on_source = ['unite.vim']

# 特定のコマンドを打ったら読み込む
[[plugins]]
repo      = 'thinca/vim-prettyprint'
on_cmd    = ['PP', 'PrettyPrint']

# 特定のマッピングを使ったら読み込む
# 又、gvim でしか利用しない
[[plugins]]
repo   = 'thinca/vim-fontzoom'
on_cmd = ['Fontzoom']
on_map = ['<Plug>(fontzoom-']
gui    = 1
```

などなど、他にもありますので `:h dein` してマニュアルを読んでみてください。

# NeoBundle から以降で変わったこと・気をつけること

移行時にハマった点、気付いた新機能などを書いときます。

## Neovim に対応した（らしい）

Neovim 自体使ってないので試してないのです……。非同期でインストールやアップデートできて、それはそれは快適らしいですよ。

## `depends` を書いただけではインストールしてくれない &amp; 書式もちょっと違う

これが一番ハマりました。今までは

```sh
# NeoBundle 用設定
[[plugins]]
repo    = 'vim-airline/vim-airline'
depends = ['vim-airline/vim-airline-themes']
```

と、書いとくだけで `vim-airline-themes` まで一緒にインストールされたんですが、dein.vim では個別に書く必要があります。

```sh
# dein 用設定
# depends の書式も微妙に違う
[[plugins]]
repo    = 'vim-airline/vim-airline'
depends = ['/vim-airline-themes']

[[plugins]]
repo = 'vim-airline/vim-airline-themes'
```

うーん。これなら `depends` 特に要らない気がする……。

## `if` の追加

Vim script によって読み込む条件を指定可能になりました。これはスゴイ便利です。

```sh
[[plugins]]
repo = 'elzr/vim-json'
if   = '''! has('kaoriya')'''
```

Kaoriya 版以外の時のみ、読み込んでいます。Vim の構文がそのまま書けます。

## プラグイン情報を取得するコマンドが変わった。

今までプラグインのインストールパスを調べるために

```vim
let l:path = neobundle#config#get('hoge').path
```

とかしてましたが、

```vim
let l:path = dein#get('hoge').path
```

になりました。短い！

## プラグインのインストールパスの形式が変わった

今までは指定されたディレクトリにプラグイン名が羅列されていましたが、

```sh
# NeoBundle
/plugin1.vim
/plugin2.vim
...
```

dein.vim では github.com のパス名がそのまま入るようになりました。`go get` した時みたいです。

```sh
# dein.vim
/repos/github.com/author1/plugin1.vim
/repos/github.com/author2/plugin2.vim
...
```

## unite ソースとかがばっさりなくなった

`Unite dein` という、プラグインリストが見れるものだけになりました。`neobundle/update` とか重宝してたのですが……

まだまだ開発中のプラグインなので、なくなった機能はそのうち復活するのかも知れません。

## `on_source` フックの設定方法が変わった

> // 追記ここから 2016/04/09
>
> TOML ファイルと使う場合、以下のように `autocmd` を設定するよりも、各種 `hook` を使った方が分かりやすいです。以下の記事を参照してください。
>
> [[dein.vim] hook の便利な使い方 - Qiita][dein-hook]
>
> // 追記ここまで

lazy loading 時にのみ、有効な設定を書きたい場合があります。たとえば、[vim-gista][] では Github Enterprise で使う場合、事前にコマンドを実行しておく必要があるのですが、lazy loading しているとこのコマンドは `vimrc` 内で使えません。

[vim-gista]: https://github.com/lambdalisue/vim-gista

```vim
" 「E117: 未知の関数です」と言われてしまう
call gista#client#register('GHE', 'https://github.example.com/api/v3')
```

NeoBundle ではこのようなとき、`on_source` フックを利用していたのですが、ぱっと見では何やってるのか分かりにくいものでした。

```vim
" NeoBundle 用コード
let s:hooks = neobundle#get_hooks('vim-gista')
function s:hooks.on_source(bundle)
  call gista#client#register('GHE', 'https://github.example.com/api/v3')
endfunction
```

dein.vim では以下のように書きましょう。

```vim
" どこか最初の方に書いておく
augroup MyAutoCmd
  autocmd!
augroup END

" プラグインごとに if 文を書く
if dein#tap('vim-gista')

  " 実行すべき関数
  function! s:gista_on_source() abort
    call gista#client#register('GHE', 'https://github.example.com/api/v3')
  endfunction

  " g:dein#name にはプラグイン名が入る
  execute 'autocmd MyAutoCmd User' 'dein#source#' . g:dein#name
        \ 'call s:gista_on_source()'
endif
```

Vim 標準の `autocmd` 機構を使うようになりました。NeoBundle と比べて、なるべく Vim 標準の機能を使っていることが dein.vim の速さの秘密なのかも知れません。

----

というわけで、dein.vim の使い方と NeoBundle からの移行方法についてまとめてみました。重ねて言いますが、**NeoBundle は deprecated になってしまいました**ので、これからプラグイン管理を始める方は是非 dein.vim を使いましょう！