Neovim のプラグイン開発をした学びを作りながら振り返る
Neovim を本格的に利用しはじめてから半年ほど経つのですが、とうとう自分でプラグインを作ることになりました。今回は、そのプラグイン開発において学んだことをつらつらと書き留めておこうと思います。
自分への備忘録としつつ、これからプラグイン開発を始める方にも参考になればと思います。
ちなみに作成したプラグインはこちらです。この子についてもそのうち記事を書くかもしれません。
前提
-
lua によるプラグイン開発
denops などを利用すると TypeScript で書くこともできるらしい。今回は初めてのプラグイン開発で、せっかくなので lua で書くことにしています。
結果的には他のプラグインの実装を参考にできることが多かったので lua で良かったとも思う。 -
プラグインマネージャーは lazy.nvim を利用
プラグインマネージャーによって読み込み方などは変わるので、参考にする方がいたら適宜読み替えてください。
-
筆者は lua を体系的に学んではいない
プラグインの設定をいじったりする程度の知識でスタートして、結局最後までちゃんと学ぶことはなかったです。
簡単なプラグインを作成する
簡単なプラグインを作成しながら、プラグイン開発の流れを確認していきます。
今回は example.nvim
という名前でプラグインを作成します。
プラグインに必要なファイルを作成
example.nvim
├── lua
│ └── example
│ └── init.lua
├── stylua.toml
└── .luarc.json
.luarc.json
)
ビルトインのAPIの補完が効くようにする (.luarc.json
を作成して、lua の lsp での補完がされるようにします。lua-language-server が有効になっている環境が前提です。
プラグインを作成しない場合でも ~/.config/nvim/.luarc.json
を設定しておくと、Neovim の設定をする際に補完が効くので設定して損はないと思います。
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime": {
"version": "LuaJIT"
},
"workspace": {
"library": ["lua", "$VIMRUNTIME", "${3rd}/luv/library"]
}
}
lua/example/init.lua
)
プラグインの実装 (プラグイン名が example.nvim
なのに対して、ここではディレクトリ名は example
としています。
init.lua
がエントリーポイントとなります。
ひとまず以下のようにしてみます。
local example = {}
example.setup = function(opts)
vim.print(opts)
end
return example
プラグインの大まかな作成の仕方は以上です。
プラグインを読み込む
個人的におすすめの読み込ませ方
開発中の動作確認にも便利なので、最小構成の設定を作成します。
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
{
"nabekou29/example.nvim",
dir = "~/ghq/github.com/nabekou29/example.nvim",
opts = {
message = "Hello, world !",
},
},
})
dir
を指定することで、開発中のローカルのプラグインを読み込むことができます。
開発中のプラグインを読み込む方法はいくつかありますが、特に複数マシンで Neovim を利用している場合は、次のような設定にするのがおすすめです。
local opts = {
dev = {
path = "~/ghq/github.com/nabekou29",
-- path = vim.fn.systemlist("ghq root")[1] .. "/github.com/nabekou29",
-- ↑ このようにしても良いが、nvim の起動速度に影響が出るため、可能なら全マシンで固定のパスを指定するのがよい。
patterns = { "nabekou29" },
fallback = true,
},
}
require("lazy").setup({
{
"nabekou29/example.nvim",
opts = {
message = "Hello, world !",
},
},
}, opts)
このような設定でも開発中のプラグインを読み込むことができます。
この方法のメリットは以下の通りです。
- プラグインの設定部分はローカルとリモートのどちらから取得する場合も同じになる
-
fallback = true
を設定することで、ローカルにリポジトリがある場合はローカルを読み込み、ない場合は GitHub から取得するようになる
プラグインを読み込んで起動
この時点で nvim を起動すると、起動後に以下の画像のようなメッセージが表示されるはずです。
$ nvim -u minimal-init.lua
開発の進め方
基本的にここまでできたら、あとはひたすらプラグインの実装を進めていくことになります。
作りたいプラグインによって必要な情報も大きく変わるので、具体的な情報よりは開発中に気をつけていたことを書いていきます。
私は以下のように進めていました。
実装に参考になる情報を集める
参考になりそうなプラグインの実装を読んでみる
作りたいものが、コマンドなのか、UIなのか、作りたいものに合わせて、自分がよく使っているプラグインの実装を読んでみると良いと思います。
ディレクトリ構造だったり、参考になるものが多いです。1つだけでなく多くのプラグインの実装を読んでみると、実装のイメージが掴めると思います。
ChatGPT に聞いてみる
何すればいいかわからないって詰まった時には、ChatGPT に聞くようにしていました。
Neovim のプラグインに関する日本語の情報は少ないのか、ググってもなかなか情報が見つからないことが多かったので、ChatGPT はかなり頼りになりました。
help
を読む
多分一番重要かつ頼りになるが、サボりがち。
参考にした実装が古かったり、ChatGPT も最適解を出してくれないことがあるので、最終的に help
を読むことが大事だと思います。
telescpoe を入れている場合は、:Telescope help_tags
で検索すると便利です。
vim.fn.xxx
, vim.tbl_xxx
など、組み込みの関数や API が意外と便利だったりします。
既存のプラグインの実装だと自前で実装していることもあったりするのですが、割と組み込みの関数で代替できることも多いです。
型アノテーションを書く
個人の方針もあると思いますが、がっつりプラグインを作る場合は型アノテーションを書いておくと良いと思います。
参考: https://github.com/LuaLS/lua-language-server/wiki/Annotations
プラグイン向けのプラグインを使う
lua では LuaRocks というパッケージマネージャーがあるのですが、使わないのが一般的なようです。
ただ、プラグインとして提供されているパッケージのようなものはあります。
よく見るものだと、plaenary.nvim があります。
こういったものはそこまで多くないように思いますが、探してみる価値はあると思います。
開発中の動作確認・デバッグ方法
プラグインの再読み込み
-
lazy.nvim の機能で再読み込み
:Lazy reload {plugin_name}
で再読み込みできます。ただし、プラグインの実装次第では理想的な再読み込みができないことがあるかもしれません。基本的にはうまく動くと思います。
-
Neovim を再起動
一番確実な方法。セッションを活用すればバッファの状態を保持できるのでそれほど不便ではなく、基本はこの方法を利用していました。
普段はセッションはあまり使わないって人は、以下の記事が参考になるかもしれません。(私は試していません 🙇)
プラグインの公開
基本は github にあげるだけですが、追加で dotfyle.com に登録すると、他の方にも使ってもらいやすくなるかもしれません。
終わりに
まとまりのない内容になってしまいましたが、Neovim のプラグイン開発において学んだことを書き留めておきました。
読んだ人に何か一つでも参考になることがあれば幸いです。
Discussion