vim9 scriptに対応するためにしたこと
vimのプラグイン管理用にpathogen.vimとかvim-plugとか使っている方が多い?のかなと思いますが、
私は自作の簡易プラグイン管理スクリプトを書いて使っています。
動きは単純で設定ファイル通りにプラグイン用ディレクトリへgit clone したりするだけの簡単なものなのですが、
当然遅延ロードなどのリッチな仕組みは用意していないので、起動までの時間が若干かかっていました。
vim9 scriptは公式ドキュメントによると
10倍から100倍の実行速度の向上が期待できます。
とのことなのでvim9 scriptに書き直してみました。
その際にやったことをまとめます。
TL;DR
公式ドキュメントにVim scriptからの変更点がまとまっています。
正直これ読めば十分です。
vim-startuptime
vimの起動時間を計測するためには--startuptime
オプションを使えばよいのですが、複数回計測したりとか、時間のかかっている順にソートしたりとか
面倒なのですがそのあたりを一括でやってくれるツールとしてvim-startuptime
があります。
Go製のツールで実行すると以下のように結果が出力されます。
このキャプチャだと、10回vimを起動させた際の 平均、最大、最小の起動時間と各スクリプトに読み込みにかかった時間が表示されています。
Before
vim scriptのままの状態で計測すると以下のような結果でした。
Total Average: 693.800000 msec Total Max: 837.000000 msec Total Min: 573.000000 msec AVERAGE MAX MIN --------------------------------- 602.000000 757.000000 475.000000: $HOME\_vimrc 588.400000 746.000000 463.000000: C:\Users\sabiz\vimfiles\conf\loader.vim 21.000000 61.000000 15.000000: loading packages 15.133333 25.000000 13.000000: C:\Users\sabiz\vimfiles\conf\plugin/molokai.vim 12.933333 33.000000 8.000000: BufEnter autocommands 11.666666 31.000000 9.000000: C:\Users\sabiz\vimfiles\pack\plugin\start\vim-lsp-settings\plugin\lsp_settings.vim 10.200000 29.000000 7.000000: loading plugins 5.533333 12.000000 5.000000: C:\Users\sabiz\vimfiles\pack\plugin\start\vim-gitgutter\plugin\gitgutter.vim 5.066666 7.000000 4.000000: parsing arguments 4.466666 8.000000 4.000000: C:\Program Files\Vim\vim90\syntax\syntax.vim
Top10までの抜粋しましたが、2番目に時間のかかっているloader.vim
が今回vim9 scriptに書き換える対象です。
3番目との差は明らかですねw
作りが良くないという説もありますが、vim9 scriptに書き換えて速度アップをはかります。
vim9 scriptへ書き換え
vim scriptとvim9 scriptは同一のファイル内に共存することができるようです。
そのため今回は以下のようにして、vimのバージョンを確認し9以降であればvim9 scriptを呼び出すようにしました。
(vim9 scriptが有効かどうかだけを判定する方法はあるんだろうか?)
if v:version >= 900 call {vim9 script} else call {vim script} endif
書き換えの対象を以下の関数にします。
以下関数をvim9 script化します。
function! s:installAndUpdatePlugin() abort let job_list = [] let idx = 0 for k in s:plugin_names let clonePath = s:plugin_path . k if !isdirectory(clonePath) let cmd = 'git ' . 'clone https://github.com/' . g:plugin_list[idx] . ' ' . clonePath call echoraw("\x1b[33mNew:\x1b[0m\t".k."\n") elseif s:update_plugin let cmd = 'git --git-dir='.clonePath.'/.git pull origin' call echoraw("\x1b[33mUpdate:\x1b[0m\t".k."\n") else let cmd = 'git' endif let job = job_start(cmd, #{in_io: 'null', out_io: 'null', err_io: 'null'}) call add(job_list, #{name: k, job: job, pos:idx}) let idx += 1 endfor call echoraw("\x1b[s") " Save cursor pos " Wait jobs while empty(job_list) == 0 function! s:checkJobStatus(idx, val) let st = job_status(a:val.job) if st == 'dead' let info = job_info(a:val.job) if info.cmd[0] == 'git' " ignore return 0 endif let exitval = info.exitval call echoraw("\x1b[u\x1b[".a:val.pos."A") " Restore cursor pos & Move cursor if exitval == 0 call echoraw("\x1b[34mSuccess:\x1b[0m\t".a:val.name."\n") else call echoraw("\x1b[31mFailed:\x1b[0m\t".a:val.name."\n") echon " " endif endif return st !=# 'dead' endfunction call filter(job_list, function("s:checkJobStatus")) endwhile endfunction
この関数はプラグインのリストを見て、
プラグインがプラグインディレクトリになければgit clone
し既に存在し
アップデート設定が有効であればgit pull
をjobで非同期に並列で行うようになっています。
関数定義
vim9 scriptでは関数はdef
で定義します。
+ def! InstallAndUpdatePlugin() - function! s:installAndUpdatePlugin() abort ... + enddef - endfunction
※)diff形式のシンタックスにしています。
変数宣言
let
ではなく var
で宣言します。
また、代入する際にlet
を書く必要はありません。
+ var idx = 0 - let idx = 0 for k in s:plugin_names ... + idx += 1 - let idx += 1 endfor
文字列の結合
.
ではなく..
を使用します。
vim scriptでも..
使えたような気がするので、
変更点としては.
での結合ができなくなったということでしょうか。
- 'git ' . 'clone https://github.com/' . g:plugin_list[idx] . ' ' . clonePath + 'git ' .. 'clone https://github.com/' .. g:plugin_list[idx] .. ' ' .. clonePath
コメント
"
ではなく#
を使います。
- " Wait jobs + # Wait jobs
ラムダ式
元のvim scriptの方では使ってないのですが、
vim9 scriptにするにあたりインナー関数になっているところをラムダ式に変更しました。
vim scriptでは () -> {}
というような書き方をするようです。
# vim9 scriptでの書き方 filter(job_list, (i, value) => { ... })
最終結果
以上がvim9 scriptにするためにしたことです。
最終的に先ほどのvim scriptの関数は以下のようになりました。
変換+若干の処理変更が入っていますがほぼ同じ処理内容です。
def! InstallAndUpdatePlugin() var job_list = [] var idx = 0 for k in s:plugin_names var clonePath = s:plugin_path .. k var cmd = '' if !isdirectory(clonePath) cmd = 'git ' .. 'clone https://github.com/' .. g:plugin_list[idx] .. ' ' .. clonePath echoraw("\x1b[33mNew: " .. k .. "\x1b[0m") echoraw("\n") elseif s:update_plugin cmd = 'git --git-dir=' .. clonePath .. '/.git pull origin' echoraw("\x1b[33mUpdate: " .. k .. "\x1b[0m") echoraw("\n") else continue endif var job = job_start(cmd, {'in_io': 'null', 'out_io': 'null', 'err_io': 'null'}) add(job_list, {'name': k, 'job': job, 'pos': idx}) idx += 1 endfor echoraw("\x1b[s") # Save cursor pos # Wait jobs while empty(job_list) == 0 filter(job_list, (i, value) => { var st = job_status(value.job) if st == 'dead' var info = job_info(value.job) if info.cmd[0] == 'git' # ignore return false endif var exitval = info.exitval echoraw("\x1b[u\x1b[" .. value.pos .. "A") # Restore cursor pos & Move cursor if exitval == 0 echoraw("\x1b[34mSuccess: " .. value.name .. "\x1b[0m") echoraw("\n") else echoraw("\x1b[31mFailed: " .. value.name .. "\x1b[0m") echoraw("\n") echon " " endif endif return st !=# 'dead' }) endwhile enddef
メインのロジックはほぼそのままで周辺の書き方を少しづつ変えた形ですね
めっちゃ楽でしたw
After
今回は高速化を狙ってvim9 scriptに書き換えていました。
vim9 script版で計測してみました。
Total Average: 113.066666 msec Total Max: 119.000000 msec Total Min: 110.000000 msec AVERAGE MAX MIN ------------------------------ 35.466666 40.000000 34.000000: $HOME\_vimrc 23.333333 24.000000 22.000000: C:\Users\sabiz\vimfiles\conf\loader.vim 17.200000 19.000000 13.000000: loading packages 12.666666 13.000000 12.000000: C:\Users\sabiz\vimfiles\conf\plugin/molokai.vim 10.866666 13.000000 9.000000: BufEnter autocommands 9.200000 10.000000 9.000000: C:\Users\sabiz\vimfiles\pack\plugin\start\vim-lsp-settings\plugin\lsp_settings.vim 8.266666 10.000000 6.000000: loading plugins 5.000000 5.000000 5.000000: C:\Users\sabiz\vimfiles\pack\plugin\start\vim-gitgutter\plugin\gitgutter.vim 4.733333 6.000000 4.000000: parsing arguments 3.533333 4.000000 3.000000: C:\Program Files\Vim\vim90\syntax\syntax.vim
以上のような結果でした。
対応前後で表にまとめると以下のような感じです。
項目 | Before | After |
---|---|---|
Total Average | 693.8 ms | 113.1 ms |
Total Max | 837.0ms | 119.0 ms |
Total Min | 573.0ms | 110.0 ms |
loader.vim AVERAGE | 588.4 ms | 23.3 ms |
loader.vim MAX | 746.0 ms | 24.0 ms |
loader.vim MIN | 463.0 ms | 22.0 ms |
うん、比較するのがバカバカしいほどに高速化されましたねw
※ちなみに、計測時は上記のスクリプトでいうところのgit clone
もpull
も実行されないケースで試しています。
外部コマンド入れたらそれに依存した時間になりますからね。
今回vim9 scriptに書き換えたスクリプトは以下に置いています。
物好きな方はどうぞ参考にしてください。
まとめ
控えめに言ってvim9 script速えぇ
(今までが遅すぎたという見方はしちゃダメだ)