Neovimで非同期でシェルコマンドを実行して完了後に自動で閉じる技術
Neovimのターミナル設定の個人的まとめです。
Neovimで:terminal echo hello world
のようにシェルコマンドを実行すると、[Process exited 0]
のようにステータスコードを表示して実行を停止します。
このあと、何らかのキーを入力することでターミナルが終了します。
コマンドの終了ステータスが出る
echo hello world
などであれば表示を見たいので実行が停止してくれたほうが良いのですが、:terminal
で開始したインタラクティブシェルでexit
を実行したときなど、そのまま終了したほうが都合の良い場合もあるでしょう。
exitしたんだからそのまま終了してほしい
この場合、:terminal
コマンドではなく、termopen()
関数を使用すると即終了することができます。
TerminalAutoclose
コマンド
以下の設定を使います。
function! s:terminal_autoclose(cmd) abort
let bn = bufnr()
let cmd = get(a:, 'cmd', '')
if cmd == ''
let cmd = &shell
endif
let opts = { 'on_exit': { -> { execute(bn .. 'bwipeout', 'silent!') } } }
call termopen(cmd, opts)
normal! G
endfunction
command! -nargs=* TerminalAutoclose call s:terminal_autoclose(<q-args>)
TerminalAutoclose
コマンドは、無引数で実行するとインタラクティブシェルを開始し、引数を渡すとそれをシェルコマンドとして実行します。
ここでのポイントは、termopen()
関数の第二引数のオプションにわたすon_exit
フックです。これは、termopen()
関数の第一引数のコマンドが終了したときに呼ばれる関数です。
ここでbwipeout
を実行することで、コマンド終了時に[Process exited 0]
を出さずにバッファを消去(つまり、自動で終了)できます。
利用例
上記のTerminalAutoclose
の利用例を紹介します。
command! RunCommand botright split +enew
\ | execute 'TerminalAutoclose some_command'
\ | set bufhidden=wipe | wincmd p
-
botright split +enew
で現在のウィンドウの下に新しくターミナルウィンドウを作る -
TerminalAutoclose
でコマンドを実行 - バッファが閉じたときに自動で破棄するよう設定
-
wincmd p
で元のウィンドウにフォーカスを戻す
これにより、シェルコマンドを実行しつつ、カレントバッファで編集を続けることができます。
:!
を使う場合と異なり、時間がかかるコマンドでも編集作業がブロックされません。
ターミナルウィンドウでコマンドを直接停止した場合にはon_exit
のbwipeout
が、:only
などでウィンドウを閉じた場合にはbufhidden=wipe
が発動し、バッファが破棄されます。
一定時間で自動終了するコマンドの例
一定時間で実行終了する例です。
gh graph
を実行し、3秒後に自動終了します。
即終了すると結果を見ることができないので、&& sleep 3
を入れています。
command! GhGraph botright 10 split +enew
\ | execute 'TerminalAutoclose gh graph && sleep 3'
\ | set bufhidden=wipe | wincmd p
なにか情報をちらっと確認したい場合などに便利です。
ここではsleep
で3秒待機させていますが、元から一定時間後に終了するコマンドに対しても使えます。
GhGraph
実行し続けるコマンドの例
終了するまで実行し続ける例です。
nyancat
を実行します。
command! Nyancat botright split +enew
\ | execute 'TerminalAutoclose nyancat'
\ | set bufhidden=wipe | wincmd p
エディタの下でなにかのコマンドを動作させておきたい場合に使えると思います。
watch
系のコマンドとは相性が良さそうです。
開発サーバーを実行する場合などはbufhidden=hide
のほうが良いかもしれません。
Nyancat
splitではなくフローティングウィンドウで使う例
split
ではなくフローティングウィンドウで実行することも可能です。別記事に記載します。
参考
こちらの実装を一部参考にしました。
Discussion