Lua製Neovim plugin開発でhot-reloading的な体験を得る

公開:2020/10/28
更新:2020/10/28
1 min読了の目安(約1100字TECH技術記事

一度requireされたモジュールはキャッシュされるため、
そのモジュールのコードを変更してもpluginの動作には反映されない。
再起動すれば当然反映されるがめんどくさすぎる。

以下のようにキャッシュを消すと次のrequireではファイルから読んでくれる。

package.loaded["myplugin.mymodule"] = nil

これを利用して、保存時にplugin配下のモジュールのキャッシュを全部削除することでhot-reloading的な体験が得られる。
⚠ Vim scriptのplugin同様にネームスペースとして固有のplugin名を使っていることが前提。

以下がコード例。開発中はlet g:myplugin_debug = 1を設定して使う。
例だとlua/myplugin/mymodule.luaを変更して保存するだけでMypluginコマンドを試行錯誤できる。
💭 ちなみにcommandやaugroupの定義はluaだとまだ面倒なのでVim scriptで書いている。

plugin/myplugin.vim
command! Myplugin lua require("myplugin/mymodule")()

if get(g:, 'myplugin_debug', v:false)
  augroup myplugin_dev
    autocmd!
    execute 'autocmd BufWritePost' expand('<sfile>:p:h:h') .. '/*' 'lua require("myplugin/cleanup")'
  augroup END
endif
lua/myplugin/cleanup.lua
local plugin_name = vim.split((...):gsub("%.", "/"), "/", true)[1]
local dir = plugin_name .. "/"
for key in pairs(package.loaded) do
  if (vim.startswith(key, dir) or key == plugin_name) then
    package.loaded[key] = nil
  end
end

リポジトリのコードに含めずとも、autocmdのpath部分を書き換えればlocal用の設定として動作する。
個人的にはテストコードのafter_eachでもcleanup.luaを実行するため、
また、どの環境でも変数1つ定義するだけで開発するためにリポジトリのコードに含めている。