Vim 8.0用のプラグインマネージャを作ってみた話

# 概要

Vim 8.0 では、Vim 本体にプラグイン/パッケージを扱うための機能が追加され、さらに外部プロセスを実行して非同期に通信を行う、ジョブ・チャンネル機能が追加されました。
今回、これらの機能を活用した Vim 8.0（およびNeovim）用のプラグインマネージャを作ってみました。

# プラグインマネージャの歴史

本題に入る前に、Vim のプラグインマネージャの歴史を軽くおさらいしてみましょう。（以下の分類は、私による勝手な分類です。）

## 第 1 世代

Vim には、[Vimball](http://vim-jp.org/vimdoc-ja/pi_vimball.html#vimball) と [GetLatestVimScripts](http://vim-jp.org/vimdoc-ja/pi_getscript.html#glvs) という標準プラグインが付属しており、これを使えば対応プラグインを簡単にインストールしたり、更新することができました。
しかし、これらに対応していないプラグインの管理は困難でした。[`'runtimepath'`](http://vim-jp.org/vimdoc-ja/options.html#%27runtimepath%27) の管理は手動だったため、すべてのプラグインを単一ディレクトリにインストールすることもよくありました。そのためプラグイン同士のファイルは簡単に混ざってしまい、正しく管理するのは困難でした。

## 第 2 世代

[pathogen](https://github.com/tpope/vim-pathogen) や [unbundle](https://github.com/sunaku/vim-unbundle) などがあります。特定のディレクトリ構成にしたがってプラグインを配置し、いくつかの設定を書けば、自動で `'runtimepath'` を更新してくれるようになりました。
簡単にプラグインごとにディレクトリを分けることができるようになったため、プラグインの更新、削除などが簡単に行えるようになりました。

## 第 3 世代

[Vundle](https://github.com/VundleVim/Vundle.vim) や [NeoBundle](https://github.com/Shougo/neobundle.vim) などがあります。`'runtimepath'` の管理に加え、GitHub などから自動でプラグインをダウンロードしてインストールしてくれるようになりました。


## 第 3.5 世代

[vim-plug](https://github.com/junegunn/vim-plug) や [dein.vim](https://github.com/Shougo/dein.vim) など、第 3 世代の発展形です。速度などに重点を置いて開発されているようです。


## Vim 8.0 のパッケージ機能

Vim 8.0 では、pathogen などと同様の `'runtimepath'` 管理機能が Vim 本体に搭載されました。
パッケージとは 1 つまたは複数のプラグインをまとめたものであり、それを [`'packpath'`](http://vim-jp.org/vimdoc-ja/options.html#%27packpath%27) オプションで指定したディレクトリに配置することで、Vim の起動時に自動でロードしたり、[`:packadd`](http://vim-jp.org/vimdoc-ja/repeat.html#:packadd) コマンドを使用して必要な時に手動でロードすることができるようになりました。
詳細は、[`:help packages`](http://vim-jp.org/vimdoc-ja/repeat.html#packages) を参照してください。


# minpac

今回、Vim 8.0 のパッケージ機能と Job 機能を活用した最小限のプラグインマネージャを作ってみました。

* [minpac](https://github.com/k-takata/minpac)


## 開発コンセプト

* Windows, Linux 対応。
* Vim 8.0 のパッケージ機能を活用。
* Vim 8.0 の Job 機能を活用した、プラグインの並列インストール・更新。
* シンプルかつ最小限度の機能。
* 高速。
* 10～20個程度の少数プラグイン向け。

前述のとおり Vim 8 では、`'runtimepath'` 管理機能が Vim 本体に内蔵されているため、minpac では `'runtimepath'` の管理は一切行っていません。プラグインの自動ダウンロード（インストール）、アンインストールのみを処理するようになっています。
これにより、minpac 自体の実装を非常にシンプルにすることができました。それに伴い、minpac のロードも高速 (1ms 以下) になっています。

なお、現時点では以下のような機能の対応は予定していません。

* 見栄えの良い更新画面。
* ~~プラグインのインストール後のコマンド自動実行。~~ v1.0で対応しました。
* Vim 7.4 以前への対応。


## インストール例

`'packpath'` オプションで指定されたディレクトリのうち、先頭のディレクトリの下に、`pack/minpac/opt/` ディレクトリを作成し、そこに minpac を clone してください。

Windows の場合:

```cmd
cd /d %USERPROFILE%
mkdir vimfiles\pack\minpac\opt
cd vimfiles\pack\minpac\opt
git clone https://github.com/k-takata/minpac.git
```

Linux, macOS の場合:

```sh
mkdir -p ~/.vim/pack/minpac/opt
cd ~/.vim/pack/minpac/opt
git clone https://github.com/k-takata/minpac.git
```


## 設定例


```vim:.vimrc
packadd minpac

call minpac#init()

" minpac は、`packadd` でロードできるように {'type': 'opt'} を指定する必要がある。
call minpac#add('k-takata/minpac', {'type': 'opt'})

" 他のプラグインはここに追加
call minpac#add('vim-jp/syntax-vim-ex')
...

" プラグインを直ちにロードするには以下を有効化。
"packloadall
```

`'packpath'` の先頭に `~/.vim` を追加すれば、Windows でも `~/vimfiles` の代わりに `~/.vim` ディレクトリを使うことができます。

```vim:.vimrc
set packpath^=~/.vim
packadd minpac

call minpac#init()
...
```


## 使用例

プラグインをインストール、あるいは更新する:

```vim
call minpac#update()
```

使用していないプラグインを削除する:

```vim
call minpac#clean()
```

## 高度な使い方

興味深いことに、Vim の起動時に minpac を毎回ロードする必要がありません。他のプラグインマネージャとは異なり、プラグインをインストール、更新、削除するときさえロードされていれば良いのです。これは minpac 自身は `'runtimepath'` の設定を行わないからです。
minpac をロードして、`.vimrc` を再読み込みしてプラグインの情報を登録してから `minpac#update()` や `minpac#clean()` を実行するようなコマンドを定義することで、本当に必要なときだけ minpac をロードすることができます。
（これにより起動の高速化や省メモリ化が期待できるかもしれません。）

```vim:.vimrc
if exists('*minpac#init')
  " minpac がロードされている場合
  call minpac#init()
  call minpac#add('k-takata/minpac', {'type': 'opt'})

  " 追加のプラグイン
  call minpac#add('vim-jp/syntax-vim-ex')
  ...
endif

" プラグインの設定はここに記載
...

" プラグインを更新/削除するためのユーザーコマンドの定義
" いずれも、minpac をロードし、.vimrc を再読込してプラグインの情報を
" 登録してから、作業を実行する。
command! PackUpdate packadd minpac | source $MYVIMRC | call minpac#update()
command! PackClean  packadd minpac | source $MYVIMRC | call minpac#clean()
```

当然 `.vimrc` は再読込可能になっている必要があります。

## 今後

vim-plug や dein.vim などとの速度比較は今後の課題です。

また、minpac の開発過程で、Vim 本体にあると便利だと判明した機能があれば、適宜本体にフィードバックしていく予定です。