読者です 読者をやめる 読者になる 読者になる

Vim script での依存ライブラリのバージョン管理について (vital.vim が生まれた経緯とこれまでのあらすじ)

何となく自分の考えを整理するためにつらつら書いた。 Vim プラグイン開発者にとっては目新しい情報はないかもしれないけど、vital.vim の最近の動向を知りたい人や、これから Vim プラグイン作りたい人にとっては有用かもしれないと思ったので公開してみる。


vital.vim が作られた経緯は、ざっくり言うと Vim script で異なるプラグインから同じプラグインの異なるバージョンをそれぞれ呼び分けたいという要求が日本の Vim プラグイン開発者の間で高まっていた事だった。 JavaScript で言う RequireJS のようなモジュールロードのシステムは Vim script には存在しないため、無ければ自分たちで作るしかなかった。 そうして作られたのが vital.vim (モジュールのローダー部分はほぼ thinca さんが実装)。 バージョンの違いを意識してモジュールロードできる機構を備えた Vim script ライブラリは、現状 vital.vim 以外に存在しないと思われる。

JavaScript なら最悪そういったモジュールシステムが無くてもURLにバージョン番号を含める事で違うバージョンをロードする事は回避できる。 というよりそもそも呼ばれるコンテキストが自分で作ったHTMLのページに完結しているので外部から呼ばれる心配はしなくていい。

しかし Vim script では現状 autoload の機能を直接(autoload関数を呼ぶとか)使うと、異なるバージョンを呼び分ける方法がない。 runtimepath (プラグインにとっての環境変数 $PATH のようなもの) に複数バージョンのライブラリのスクリプトファイルが存在していると、最初に見つかったスクリプトがロードされる。 vital.vim は独自のモジュールシステムによりこれを解決した。

例えば関数は全てスクリプトローカル関数として書き、先頭がアンダーバーで始まらない関数はエクスポートされる (例:s:public_func())。 逆にアンダーバーで始まる場合はエクスポートされない (例:s:_private_func())。 またモジュール初期化時に実行される関数(s:_vital_loaded())や依存するモジュールのリストを定義する関数(s:_vital_depends())など、いくつかの特殊な関数をモジュールのスクリプトファイル *1 の中で定義できる。


ただ vital.vim では異なるバージョンのモジュールを呼び分けるために、使われるモジュールのバージョンの解決をしなくてはならない。 その為に少なくないオーバーヘッドが生じていて、Vim の起動時にモジュールのロードがされると Vim の起動が遅くなったりする。

そのため vital.vim を使っているプラグインは起動の時(plugin/**/*.vimが読み込まれる時)はなるべく vital モジュールをロードしないようにして、コアの機能が使われる(autoload/**/*.vimの関数が呼ばれる等の)段階になって初めて vital モジュールをロードする、というのが定石になっている。

しかし autoload 下であっても大量の vital モジュールを同時に読み込んだ場合はユーザが体感できるほど読み込みが遅くなってしまう問題があった。 この問題にぶち当たったプラグインの一つが easymotion.vim *2

このプラグインでも最近 vital が使われるようになったが、ユーザから遅いという声が上がるようになり、それを受けて easymotion.vim のコントリビューターでもある haya14busa さんが vital のロードを大幅に高速化するプラグインを書いてくれてvital.vim 本体のローダーにも還元してくれた。 また高速化だけでなく、今までプラグインで使ってきて出てきた諸々の問題をこの際解決してしまおう、という試みを上で上げたリンクの Pull Request で haya14busa さんが行ってくれている。

これにより Vim プラグイン開発者は、Vim 起動時であっても気兼ねなく vital モジュールをロードして使えるようになる (もちろん基本的に Vim 起動時にあまり重い処理をすべきではないけど)。 既に easymotion.vimincsearch.vim では開発中の最新ローダーのブランチの vital を使っており、ユーザから高速化したとの声も上がっている *3


vital ローダー高速化の Pull Request はとても有用だけど変更点が大きくなりすぎているので少人数で回している現状だとレビューの負担が大きい (この記事を書いている間にレビューしろという話だけど…)。 HEAD から遠のいてマージの負担を増やすのも面倒なので、個人的にはぼちぼちマージの頃合いなんじゃないかな、とも思うこの頃。 vital.vim は仕組み的に最新版で破壊的変更を加えてもそれぞれのプラグインには影響を与えないし、さっさとマージして問題が起きたら直していけば良さそう。

vital.vim 便利だなー。


編集履歴

haya14busa さんから指摘をもらったので修正。

  • 削除:しかしプラグインのコア機能が起動時の段階で呼び出される場合、どうしても起動時に vital モジュールをロードする必要性が出てくる。
  • 追加:しかし autoload 下であっても大量の vital モジュールを同時に読み込んだ場合はユーザが体感できるほど読み込みが遅くなってしまう問題があった。

*1:ちなみにモジュールはスクリプトファイルごとに分かれていなければならない

*2:他にも incsearch.vimbrightest.vim 等もそうだった気がする

*3:easymotion.vim ユーザからの声incsearch.vim ユーザからの声