NeoVimでコマンドをUIをブロックせずに非同期に実行する方法

目次

  1. 背景
  2. 解決したい課題: os.executeを使うとUIがブロックされてしまう
  3. 解決方法: vim.loop.spawnを使う
  4. 補足
  5. まとめ
  6. 関連記事
  7. PR

背景

こんにちは。 かりんとうマニア(@karintozuki)です。
皆様、NeoVimのプラグイン開発やってますでしょうか。
私はNeoVimからChatGPTと会話できるプラグインを作っています。
https://github.com/karintomania/nvim-ai-chat
よかったら使ってみてください(宣伝)

今回はそんなプラグイン開発の上で役立つTipsを紹介します。

解決したい課題: os.executeを使うとUIがブロックされてしまう

NeoVimでターミナルコマンドを実行する際、os.executeが使えます。
しかし、これには問題があり、UIがコマンド終了までブロックされてしまいます。

以下のサンプルコードを実行すると、NeovimのUIが3秒間操作を受け付けなくなってしまいます。

sample.lua
1
2
3
4
os.execute("sleep 3")
-- 3秒経つまで実行されない、その間UIはブロックされる
vim.print("sleepが終わりました")

これではユーザーが他の操作を行うことができず、使い勝手が悪いです。

今回はこの問題を解決する方法を紹介します。

解決方法: vim.loop.spawnを使う

NeoVimには非同期処理を行うための便利なモジュールであるvim.loopがあります。
次のサンプルコードを見てください。これを実行すると、UIはブロックされずにsleepコマンドが実行されます。また、uv.spawn以降のコードはコマンドの終了を待たずに実行されます。

sample.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
-- 実行するコマンドの引数。ここでは3秒sleepさせるため、3
local options = {"3"}
local handle, pid = uv.spawn("sleep", {
args = options,
}, function(code, signal) -- コマンド終了時に実行されるコールバック
vim.print("sleepが終わりました")
end)
vim.print("プロセスID:"..pid)

-- 実行結果
-- プロセスID 9999
-- sleepが終わりました

このように、vim.loop.spawnを使うことで、UIをブロックせずにコマンドの非同期実行が可能になります。コードの実行結果は、コールバック関数内で処理することができます。

補足

vim.loop.spawnで実行できるのはターミナルで実行するコマンドだけです。

例えば普通のLua関数を定義して、それをUIブロックせずに呼び出す方法は私は見つけられませんでした。
vim.loop関連の関数で非同期な関数実行を実現できますが、UIはプログラムが終了するまでブロックされてしまいました。もし、良い方法を知っている人がいたらご連絡ください。

vim.loopの公式ドキュメントがとても学びが深いです。

https://neovim.io/doc/user/lua.html#vim.uv

ドキュメント内ではvim.uvとして紹介されていますが、私の環境(NVIM v0.9.1)ではvim.loopとしないと動きませんでした。

まとめ

今日はvim.loop.spawnの解説でした。
それじゃ今日はこの辺で。

関連記事

こちらの記事もおすすめです。

Luaで最小構成のNeoVimのプラグインを作る

PR

あなたの会社はあなたの技術を評価してくれていますか?
技術力を高めようと頑張っているのに、
技術が評価されないような会社にいたらそれは良い環境なのでしょうか?
エンジニアとして技術を高めたいのなら環境を選ぶことも大事です。

レバテックキャリア
エンジニアとして働いていて実務経験があるなら、
求人数の充実具合からレバテックキャリアがおすすめです。
IT転職ではデファクト・スタンダードですね。
▼レバテック キャリア 登録はこちら▼


Tech Clips
Tech Clipsは年収500万以上&自社サービスを持った会社に特化した求人サイトです。
首都圏限定になってはしまいますが、
収入を増やしたい、自社サービスを持った企業への転職をしたい人におすすめです。

▼Tech Clips 登録はこちら▼