Neovim でコマンドの実行結果をバッファに出力する方法

概要

前置き

Neovim でコマンドを実行したとき、実行結果が1行の文字列ならコマンドラインに結果が表示されるのですが、結果が Table 型だと table 0x... と表示されて中身を確認できません。

これではデバッグなどに支障をきたしますので、試行錯誤してコマンドの実行結果をバッファに出力させることに成功しましたので、その方法を備忘録としてまとめます。

実装方法

コマンドの実行結果が Table 型であっても、表示するだけなら print()vim.inspect() を組み合わせれば表示させられます。以下のコードはその実例で、その下の画像は実行結果です。

1lua print(vim.inspect(vim.api.nvim_get_autocmds({pattern={'Ddu:uiDone'}})))

同じコマンドを vim.inspect() を使わずに実行した場合、実行結果はコマンドラインに table: 0x... と表示されます。

1-- `vim.inspect()` を使わない場合の表示
2lua print(vim.api.nvim_get_autocmds({pattern={'Ddu:uiDone'}}))
3-> table: 0x7faa0fcb7978

ただ、この方法は、毎回 lua print(vim.inspect(~~~)) と入力しなければならない上、何らかのキーをタイプすると結果が消えてしまいます。そこで、コマンド実行結果をバッファに出力させる方法を調べている時に発見した先例に従って、nvim_create_user_command を使って独自コマンドの Redir を作成し、その引数としてコマンド文字列を渡すと実行結果が新しいバッファで開かれるという形にしました。

最初に作った設定は以下のとおりです。

1vim.api.nvim_create_user_command('Redir', function(ctx)
2  local lines = vim.split(vim.fn.execute(ctx.args), '\n', { plain = true }))
3  vim.cmd('new')
4  vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
5  vim.opt_local.modified = false
6end, { nargs = '+', complete = 'command' })

引数で渡されたコマンド文字列を vim.fn.execute() に渡して実行し、その結果を vim.split()nvim_buf_set_lines() で処理できる形式に変換し、それから vim.cmd('new') で新しいバッファを作成したら nvim_buf_set_lines() でそのバッファに実行結果をセットしていました。

しかし、コマンド間違いなどで vim.fn.execute() の実行結果がエラーになると vim.split() に渡される値が nil になるようで、新しいバッファに何も表示されません。そこで、vim.fn.execute() の実行結果がエラーになったらエラーメッセージをバッファに表示させるため、以下のとおりコードを修正しました。

1vim.api.nvim_create_user_command('Redir', function(ctx)
2  local pcall_result, function_return = pcall(vim.fn.execute, ctx.args)
3  vim.cmd('new')
4  vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(function_return, '\n', { plain = true }))
5  vim.opt_local.modified = false
6end, { nargs = '+', complete = 'command' })

最初の設定との違いは、pcall 関数を使ってエラーメッセージを function_return に格納するようにしたことです。こうすると vim.fn.execute() の実行結果がエラーになってもエラーメッセージが function_return に格納されて vim.split() で処理できますので、新しいバッファに何も表示されないという事態を避けられますし、エラーメッセージがバッファに出力されます。

実行例

1Redir :=vim.api.nvim_get_autocmds({pattern={'Ddu:uiDone'}})
1-- `}` を1つ減らしてエラーになるようにしています
2Redir :=vim.api.nvim_get_autocmds({pattern={'Ddu:uiDone'})