Vimプラグインマネージャー『dpp.vim』への移行と設定方針
始めに
年末年始の休みでNeovimプラグインマネージャをdein.vim
からdpp.vim
に移行しました。
dein.vim
自体も難しいプラグインマネージャではありますが、dpp.vim
はさらに上を行く難易度です。
また使用者によって構成や設定方法は違う可能性があり、応用力が求められるプラグインマネージャです。
今回の移行作業に際して、私の構成や設定方針を記事として残しておきます。
基本的な設定や使い方に関しては、Shougoさんの記事をご覧ください。
もくもく会はいいぞ
年末年始の2023-12-28にvim-jpで開催された 『新宿もくもく会』 では、このdpp.vim
の移行作業というテーマで参加しました。
当日以内での移行とは行きませんでしたが、もくもく会という集中できる空間もあり、よい刺激を受けながら作業ができました。
当日は悩んでいる様子しか見せることができませんでしたが、無事dpp.vim
への移行ができたという報告も兼ねさせていただきます。
もくもく会はいいぞ!
構成
過去の構成・設定について
dpp.vimの拡張について
現在使用しているdpp.vim
の拡張は次のとおりです。
-
dpp-ext-lazy
- プラグインの遅延起動処理を追加する拡張
-
dpp-ext-toml
- toml読み込みを使用できるようになる拡張
-
dpp-ext-installer
- プラグインのインストール処理が可能になる拡張
-
dpp-protocol-git
- Gitのcloneによるプラグインインストールを可能とする拡張
この拡張の構成をdein.vim
と比較した場合、ローカルプラグインの読み込み機能を使用しない構成になります。
ローカルプラグインの読み込み機能はdein.vim
の頃から使ってなかった機能だったので、このように使う機能を選択できるのがdpp.vim
の良さですね。
まだ試せてはいないですが、この他にも拡張が作成されているようなので、設定のしがいがありそうですね。
ディレクトリ構造
ディレクトリ構成は次のようにしています。
~/.config/nvim
├── after
├── denops
+├── dpp # dpp.vimの設定をまとめたディレクトリ
+├── hooks # hooks_fileをまとめたディレクトリ
├── lua
+├── rc # inlineVimrcsに追加される全体設定をまとめたディレクトリ :h dpp-option-inlineVimrcs
+├── snippets # 個人で定義したスニペット用のディレクトリ(今回は関係ない…)
+└── toml # dpp-ext-tomlで読み込むtoml用のディレクトリ
通常のruntimepath
やdenops.vim
を使用した場合を除いて、自動で探索されないディレクトリを追加しています。
対象のディレクトリにはマークを付けていますが、ディレクトリの名前のとおりの意味合いになっております。
このとおりに各種設定ファイルを配置し、init.lua
やdpp.vim
内での設定で各種ディレクトリを探索して、
すべての設定はdppのキャッシュとしてまとめられるようになっています。
設定の基本構成としては、次の流れで読み込まれていきます。
init.lua
lua/user/rc.lua
-
dpp/config.ts
rc/*.lua
toml/*.toml
-
hooks/*
※各種プラグイン毎に読み込み
今回紹介する設定では、主にlua/user/rc.lua
とdpp/config.ts
を中心に紹介したいと思います。
tomlによる設定に関しては、dein.vim
と構造は変わらないため説明を省きます。
設定
lua側の設定
init.lua
からlua/user/rc.lua
のsetup
関数を呼び出すことで、
各種ディレクトリのマークと必須プラグインのインストールまでをセットアップするようにしています。
setup
最初はNeovimで用意されているNVIM_APPNAME
への対応と、各種設定を配置したディレクトリをマークするために、グローバル変数や環境変数にセットしています。
後半の方は、dpp.vim
を使用するための設定郡で、別の関数に切り出しています。
plugin_add
一番大事な関数です。
初回起動からdpp.vim
が使えるようになるためには、自前の極小プラグインマネージャを構築する必要があります。
私の場合色々とリッチにしていることもあって、他のGitホスティングサービスからのプラグイン追加をできるようにしていますが、
現状ではGitHubからのインストールだけで十分かと思います。
プラグインはruntimepath
に追加されることで、プラグインとして使用可能になるため、必ずruntimepath
への追加を行なっています。
prepend
を使用することでruntimepath
の先頭にdpp.vim
として必須のプラグインが追加されるようにしています。
気持ちの問題かもしれませんが、先頭に追加するのは「そっちの方が処理は速くなるのかな…」という迷信や思いを込めています。
ここに関しては、もうすこしよい書き方があるかもしれないと模索している最中です。
dpp_setup
lua側の最後設定です。
ここでは大量にdpp.vim
用のautocmdを定義したり、自動セットアップのために涙ぐましい努力を重ねています。
自動セットアップとして初回起動時には、すべてのプラグインがインストールされてNeovimが安定して終了するように制御しています。
make_state
を実行するタイミングはdenops.vim
がプラグインとして使える状態になっていないといけません。
そのため、Shougoさんの記事ではDenopsReady
というイベントにフックする形で、make_state
を実行しています。
しかし、autocmdで書かなくてもdenops.vim
で用意されているdenops#server#wait_async
という関数が便利です。
*denops#server#wait_async()*
denops#server#wait_async({callback})
Wait asynchronously until a |DenopsReady| autocmd is fired and invoke
a {callback}. It invokes the {callback} immediately when the autocmd
is already fired. If this function is called multiple times, callbacks
registered are called in order of registration.
DenopsReadyになるまで非同期で待機します。
autocmdが起動され、{callback} が呼び出されます。
autocmdがすでに起動されたら、すぐに {callback} を呼び出します。
この関数が複数回呼び出された場合、登録されたコールバックが登録順に呼び出されます。
また、設定の編集・保存後にフックする形でcheck_files
という処理を実行しています。
これが実行されると、dpp.vim
で作成されるキャッシュの更新として、make_state
を実行してくれます。
そのためNeovimの設定ファイルだけを探索しておいて、対象のファイルを変更したときだけcheck_files
を実行するようにしています。
TypeScript側の設定
make_state
によって呼び出すTypeScriptの設定です。
基本方針として主要な処理はこのconfig.ts
ファイルで実行していますが、ファイルを集めたり、型を定義するのはhelper.ts
にまとめています。
この基本方針は、ddc.vim
とddu.vim
でも同じような方針で設定を分割しています。
ファイル探索系
各種設定ファイルを配置したディレクトリ内を探索する関数として、helper.ts
には次の関数をそれぞれ定義しています。
gatherVimrcs
gatherTomls
gatherCheckFiles
これらの関数では、dpp.vim
の設定として返して欲しい型にしてデータを返すように処理をまとめて、config.ts
では探索場所と追加の条件があれば、適宜定義しています。
これらの関数を使用するための探索場所をlua側の設定でグローバル変数や環境変数という形で定義しておくことで、denops_std
を使えば呼び出せるということです。
たとえばgatherVimrcs
の場合、~/.config/nvim/rc
というディレクトリを探索しやすくするために、vim.g.rc_dir
というグローバル変数にパスを格納しています。
これをdenops_std
で取得する場合は次のようにすると呼びだせます。
const inlineVimrcs: string[] = gatherVimrcs(
await vars.g.get(denops, "rc_dir"),
vimrcSkipRules,
);
このvars
というものを使えば、vim側で設定した変数へのアクセスが可能になります。
私の場合、dpp.vim
の依存としてまとめているdeps.ts
からインポートするようにしています。
その後、対象のディレクトリの中のファイルリストを作成するのは、deno本体のAPIのDeno.readDirSync
というメソッドを使用しています。
この探索方式はtomlファイルを探索するためにも使用していますが、gatherCheckFiles
だけは違う方法で探索しています。
checkFiles
では設定ファイルのリストを渡す必要がありますが、設定ファイルはさまざまなディレクトリに配置されている関係で、Deno.readDirSync
を使うのは少し大変です。
そのため設定の基礎ディレクトリとしてbase_dir
というグローバル変数を定義しているので、そこから再帰的にファイルを探索する方法として、globpath
というvimの関数をdenops_std
から呼び出しています。
export async function gatherCheckFiles(
denops: Denops,
path: string,
globs: string[],
): Promise<string[]> {
const checkFiles: string[] = [];
for (const glob of globs) {
checkFiles.push(await fn.globpath(denops, path, glob, true, true));
}
return checkFiles.flat();
}
denops_std
でvimに組み込まれた関数を使用する場合はfn
から使用できます。
また、globpath
という関数に関しては、:h globpath()
でご確認ください。
パーシャルクローン
dpp-protocol-git
によって、プラグインインストール時にはgit clone
を使用しますが、このときに大きなプラグインを落すときに時間が掛ってしまいます。
これを短縮する方法として、「パーシャルクローン」という方式を使ってプラグインをcloneしてくれるオプションを有効にしています。
パーシャルクローンについては次の記事が参考になります。
移行してみての感想
dpp.vim
移行は楽しかったのと同時に、使用できるまで思うような処理の制御が難しく、
dein.vim
以上においそれとお勧めできないプラグインマネージャだと思いました。
それ以上にプログラマーが使うツールということを前提にしてみれば、この難易度に妥当さを感じました。
そんな高難易度プラグインマネージャですが、高難易度という言葉にワクワクしたり、設定するのが大好きっていう人は是非ともチャレンジしてみてください!
Discussion