🔧

Neovim Luaで実行されたファイルを1つにまとめるプラグイン

2022/02/20に公開

渡したLuaの関数で実行されたファイルを1つのチャンクにまとめるプラグインを作った。

https://github.com/notomo/tracebundler.nvim

動機

Lua製プラグインやNeovim本体のバグらしき挙動に遭遇した際に、バグを再現する最小のコードを作る。
バグの原因に全く見当がつかない場合、
バグを再現する最小でないコードからコードを徐々に減らして原因を見つけるようにしている。
この作業が大変で、補助する仕組みが欲しかった。

具体的な動作

以下のようにデバッグしたい関数を渡すとLuaのチャンクの文字列を得られる。

local debug_target = function()
  if false then
    error("unreachable")
  end
  return require("tracebundler.testdata.number")
end
local bundled = require("tracebundler").execute(debug_target)
生成されるチャンク
local _tracebundler_require = {}
local _tracebundler_loaded = {}

local global_require = require
local require = function(name)
  if not name then
    return global_require(name)
  end
  local loaded = _tracebundler_loaded[name]
  if loaded then
    return loaded
  end
  local f = _tracebundler_require[name:gsub("/", "%.")]
  if not f then
    return global_require(name)
  end
  local result = f(name)
  _tracebundler_loaded[name] = result or package.loaded[name] or true
  return _tracebundler_loaded[name]
end

_tracebundler_require["tracebundler.testdata.number"] = function(...)
  return 8888 -- TRACED
end

local _tracebundler_entrypoint = function()
  if false then -- TRACED
    error("unreachable")
  end
  return require("tracebundler.testdata.number") -- TRACED
end
return _tracebundler_entrypoint()

チャンク内の関数を呼ぶようにrequireが置き換えられるため、
requireでつながっている呼び出しがチャンク内で完結する。
(逆にExコマンド経由で呼び出されている場合はチャンク内で完結しない)

また、実行された行がわかるようにTRACEDというコメントを付けているので、
不要な処理を削るヒントになる。

感想

  • requireの置き換えをシンプルにできたのがLuaっぽい
  • ファイル内の実行されてない行の削減を自動でやるのはしんどそう
  • チャンクをファイルに保存すると https://github.com/jbyuki/one-small-step-for-vimkind が動いたのでインタラクティブなデバッグと併用すると便利そう

Discussion