🚀

とても速いVimプラグインマネージャvim-jetpack

2022/02/16に公開約8,500字

https://github.com/tani/vim-jetpack

vim-jetpackはvim-plugの実装をvim8/neovim向けにモダン化させdein.vimで用いられている最適化手法を取り入れた,Packer.nvim風のコマンド郡を備えたとても高速なプラグインマネージャです。どのくらい高速なのかといえばプラグインマネージャを用いずにビルトインのプラグイン管理機能を使ったVimよりも高速です。つまり、プラグインマネージャを読み込むオーバーヘッドよりもプラグインマネージャが行う最適化によって削減される時間のほうが大きいということです。

Vimのプラグインマネージャを使っている人はもちろん、Vimのビルトイン機能でプラグインを管理しているミニマリストにも自信をもってお勧めできます。

既存のプラグインマネージャの中でも安定して高速である

速さは正義です。起動時間が速ければ速いほどユーザー体験は向上します。Vimの起動は多くのテキストエディタの中でもかなり速い部類ですが、それでも頻繁にVimの起動と終了を繰り返していると僅かな遅延が気になってきます。vim-jetpackは最速です。遅延読み込みを設定しなくても十分に高速です。なぜならvim-jetpackではdein.vimのようにプラグインインストール時に事前にプラグインのディレクトリ構造を最適化して起動時に読み込むディレクトリの数を減らし起動時間を短縮するからです。

packer.nvim, dein.vim, vim-jetpack, minpac, packer.nvim, paq.nvim,
vim-plugの6つのプラグインマネージャに対して、適当なvimrcを設定して10回起動したときの統計結果が以下の表とグラフです。単位はミリ秒です。

dein jetpack minpac packer paq plug
最小値 80.61 69.93 64.97 75.38 73.92 77.63
最大値 96.02 74.48 81.30 89.40 84.95 82.82
中央値 85.26 71.92 72.38 78.38 78.16 80.36
平均値 86.24 71.97 72.48 80.07 78.21 80.12
分散 27.09 2.07 23.99 24.56 10.83 3.57

minpacとjetpakが他の4つよりも明らかに高速に起動していることがグラフからすぐに分りますね。そこでjetpackとminpacについて表を用いて詳しく観察してみましょう。

minpacは最小値こそjetpackよりも小さく高速に起動した記録がありますが、 最大値も大きく上にブレています。minpacの最大値はpacker,paq, vim-plugの3つの どの平均起動よりも遅くなっており最大値と最小値の幅が大きく、その分散はdeinに継いで2番目に大きい値です。したがって起動が速くなることもありますが、そのは起動速度は安定しておらず 常に高速に起動しているとは言い難いです。

jetpackは最小値こそminpacには劣っていますが、中央値、平均値ともにどのプラグインマネージャよりも小さい値を記録しています。さらに特筆すべきはjetpackの記録の分散がとても小さいことです。vim-plugの分散も十分に小さいですが、jetpackの分散は圧倒的に小さいです。jetpackは分散、平均値、中央値が6つのプラグインマネージャのなかで最小であることから、 安定して常に高速に起動していることが読みとれます。

グラフィカルなインストールの進捗状況

プラグインマネージャが高速であると評されるとき、それは単に実行速度だけ表わすものではありません。ユーザーにストレスを与えず体感速度が高速であることが重要です。その点、最近の軽量プラグインマネージャは僕にとって不満でした。パッケージをインストールするコマンドを叩いたあと標示されるのは、ぶっきらぼうなメッセージのみで、いつインストールが完了するのかが予測しづらく、 実際の実行速度にかかわらず体感速度として遅いと感じていました。

vim-jetpackは、この点をvim-plug風のプログレスバー表示を備えることで解決しました。これによりユーザーは現在の処理がどのくらい進行していていつ頃完了するのかを予測できるようにして、 体感速度を向上させました。

Vim-plugとの互換性が高い

機能を絞れば大抵のソフトウェアは高速化を達成することができます。しかしvim-jetpackは機能を絞ることはせずに、vim-plugと同等のオプションを提供する努力をしています。ほとんどのvim-plugユーザーは:s/plug/jetpack/g | s/Plug/Jetpack/gを実行するだけでvim-jetpackへの移行が完了します。単純な文字列置換だけで高速化されるなら、移行しない手はないですね!

name type description
branch/ tag sring Branch/ tag of the repository to use
rtp string Subdirectory that contains Vim plugin
dir string Custom directory for the plugin
as string Use different name for plugin
do string or func Post-updat hook
on string or list On-demand loading: Commands, <Plug>, and autocommand-events
for string or list On-demand loading: File types
opt boolean On-demand loading: packadd {name}
frozen boolean Do not update

複数のDSLが使える

Vimを使う人はこだわりが強い人が多いでしょう。設定ファイルの記述もvim-plugのようなDSLが好きな人も入ればdein.vimのようにVimscriptの関数による最小のDSLが好きな人、はたまたpacker.nvimのようにLuaによる設定やpaq.nvimのような設定が好きな人もいます。vim-jetpackは、これら全ての書き方をサポートしています。

vim-plug style

packadd vim-jetpack
call jetpack#begin()
Jetpack 'junegunn/fzf.vim'
Jetpack 'junegunn/fzf', { 'do': {-> fzf#install()} }
Jetpack 'neoclide/coc.nvim', { 'branch': 'release' }
Jetpack 'neoclide/coc.nvim', { 'branch': 'master', 'do': 'yarn install --frozen-lockfile' }
Jetpack 'vlime/vlime', { 'rtp': 'vim' }
Jetpack 'dracula/vim', { 'as': 'dracula' }
Jetpack 'tpope/vim-fireplace', { 'for': 'clojure' }
call jetpack#end()

dein/ minpac style


packadd vim-jetpack
call jetpack#begin()
call jetpack#add('junegunn/fzf.vim')
call jetpack#add('junegunn/fzf', { 'do': {-> fzf#install()} })
call jetpack#add('neoclide/coc.nvim', { 'branch': 'release' })
call jetpack#add('neoclide/coc.nvim', { 'branch': 'master', 'do': 'yarn install --frozen-lockfile' })
call jetpack#add('vlime/vlime', { 'rtp': 'vim' })
call jetpack#add('dracula/vim', { 'as': 'dracula' })
call jetpack#add('tpope/vim-fireplace', { 'for': 'clojure' })
call jetpack#end()

packer style

vim.cmd('packadd vim-jetpack')
require('jetpack').startup(function(use)
  use 'junegunn/fzf.vim'
  use {'junegunn/fzf', do = 'call fzf#install()' }
  use {'neoclide/coc.nvim', branch = 'release'}
  use {'neoclide/coc.nvim', branch = 'master', do = 'yarn install --frozen-lockfile'}
  use {'vlime/vlime', rtp = 'vim' }
  use {'dracula/vim', as = 'dracula' }
  use {'tpope/vim-fireplace', for = 'clojure' }
end)

paq style

vim.cmd('packadd vim-jetpack')
require('jetpack').setup {
  'junegunn/fzf.vim',
  {'junegunn/fzf', do = 'call fzf#install()' },
  {'neoclide/coc.nvim', branch = 'release'},
  {'neoclide/coc.nvim', branch = 'master', do = 'yarn install --frozen-lockfile'},
  {'vlime/vlime', rtp = 'vim' },
  {'dracula/vim', as = 'dracula' },
  {'tpope/vim-fireplace', for = 'clojure' },
}

軽量でインストールが簡単

vim-plugの素晴しいところはパッケージマネージャ自体が1つのファイルだけで構成されていることです。これにより、HTTPリクエストが投げれる環境ならどこでもvim-plugを設定することができます。vim-jetpackもこれに倣い1つのファイルに収めています。サイズも小さいためVimの設定ファイルの一つとしてリポジトリに組み込むことも可能です。
余談ですがVimscriptは古典的な逐次インタプリタ処理をしているのでコードが小さいことで単純に速度向上にもつながっています。

  • Linux / macOS (shell)
    • Vim
      curl -fLo ~/.vim/pack/jetpack/opt/vim-jetpack/plugin/jetpack.vim --create-dirs https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim
      
    • Neovim
      curl -fLo ~/.local/share/nvim/site/pack/jetpack/opt/vim-jetpack/plugin/jetpack.vim --create-dirs https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim
      
  • Windows (cmd.exe)
    • Vim
      curl -fLo %USERPROFILE%\vimfiles\pack\jetpack\opt\vim-jetpack\plugin\jetpack.vim --create-dirs https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim
      
    • Neovim
      curl -fLo %USERPROFILE%\AppData\Local\nvim-data\site\pack\jetpack\opt\vim-jetpack\plugin\jetpack.vim --create-dirs https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim
      

またPlugと同様に1つのファイルで構成されているので,自動でインストールされるようにブートストラップ処理を書くことも可能です.

"vim
let s:jetpackfile = expand('<sfile>:p:h') .. '/pack/jetpack/opt/vim-jetpack/plugin/jetpack.vim'
let s:jetpackurl = "https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim"
if !filereadable(s:jetpackfile)
  call system(printf('curl -fsSLo %s --create-dirs %s', s:jetpackfile, s:jetpackurl))
endif

"neovim + vim
let s:jetpackfile = stdpath('data') .. '/site/pack/jetpack/opt/vim-jetpack/plugin/jetpack.vim'
let s:jetpackurl = "https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim"
if !filereadable(s:jetpackfile)
  call system(printf('curl -fsSLo %s --create-dirs %s', s:jetpackfile, s:jetpackurl))
endif
-- nvim + lua
local fn = vim.fn
local jetpackfile = fn.stdpath('data') .. '/site/pack/jetpack/opt/vim-jetpack/plugin/jetpack.vim'
local jetpackurl = 'https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim'
if fn.filereadable(jetpackfile) == 0 then
  fn.system('curl -fsSLo ' .. jetpackfile .. ' --create-dirs ' .. jetpackurl)
end

設定や操作が簡単

プラグインを使うだけならば、プラグインのインストールと更新は同時に行われても問題無いことが多いです。そこでvim-jetpackでは単一のコマンドJetpackSyncのみでインストールと更新、最適化までを一括して行うようにしています。ユーザーは設定ファイルを更新した際に JetpackSync(あるいは環境によっては Jetpack のみ) で最新の状態を維持できます。

dein.vimよりも高速なのか

もし、あなたがVimscriptについて造詣が深くvimrcを注意深く設定することができれば、dein.vimは依然として最速です。

どのタイミングでVimのイベントが発火されるのか、自動読み込みが必要となるのか、プラグイン同士の依存関係と読み込み順序が維持できているのか、これらを把握することができればより高度な遅延処理によって起動時に読み込むプラグインを極限まで遅らせることができます。

一方で高度なカスタマイズ機能を提供するためにdein.vimは色々な処理が起動時に実行されています。もし、ユーザーが単にVimのプラグインを記述して読み込みたいだけなら、このときに実行されていることの大部分は無駄な実行時間です。

vim-jetpackはカスタマイズできる機能をvim-plug相当まで減らすことでその無駄な処理を大幅に削っています。これによりdein.vimよりも高速な起動を実現しています。

まとめ

vim-jetpackは軽量最速のプラグインマネージャです。大体のケースでは移行するだけで起動時間が大幅に短縮されます。vim-plugのファンシーなインターフェースを気に入っているのではない限り移行しない手はないでしょう。vim-jetpackはあなたのvimを加速させます。

GitHubで編集を提案

Discussion

ログインするとコメントできます