ハイパーマッスルエンジニア

Vim、ShellScriptについてよく書く

Terminalの現在行をエディタで編集して実行する

長いワンライナーを打っていると編集が面倒くさい

ワンライナーじゃなくてもいいのだが、Terminalでコマンドを打っているときに、修正するときのカーソル移動が結構面倒くさい。コマンド履歴をさかのぼり、真ん中の方のコマンドだけ編集したいときなど地獄の作業だ。この世で最も無駄な時間だと言ってもいい。

https://github.com/Rasukarusan/blog-assets/blob/master/fc-command-ex/hell.gif?raw=true
ちまちまと修正する地獄の作業

Vimに入ってしまえば慣れたキーバインド、テキストオブジェクトやプラグインを駆使して爆速で編集できるのに...

Terminalの現在行をVimで編集できればいい

そう、つまり現在行をそのままVimに持っていき、編集し終わったら内容がTerminalに打たれた状態であればいい。 出来上がったのがこれ。

https://github.com/Rasukarusan/blog-assets/blob/master/fc-command-ex/demo.gif?raw=true
Vimで爆速で編集して戻る

キーバインドCtrl-wにzleのウィジェットを登録して呼び出せるようにしている。

zle(Zsh Line Editor)を使って実現できる

下記を.zshrcに書けば終了。Ctrl-wを押せば現在行をVimで編集できる。Vimを終了すれば編集した内容がTerminalに打たれている。

edit_current_line() {
    local tmpfile=$(mktemp)
    echo "$BUFFER" > $tmpfile
    vim $tmpfile -c "normal $" -c "set filetype=zsh"
    BUFFER="$(cat $tmpfile)"
    CURSOR=${#BUFFER}
    rm $tmpfile
    zle reset-prompt
}
zle -N edit_current_line
bindkey '^w' edit_current_line

やっていることは簡単

  1. 一時ファイル作る
  2. 現在行($BUFFER)を一時ファイルに書き込んでVimで開く
  3. 現在行を一時ファイルの内容で書き換える

これだけ。
zleについては昨日の記事でも触れたので詳しくはそちら。

Vimで一時ファイルを開く際に

vim $tmpfile -c "normal $" -c "set filetype=zsh"

としているのは行末に移動するためと、VimのSnippetを効かせるためにFiletypeをzshに設定している。

fcコマンドでいいんじゃないの?

似たような挙動をするものでfcコマンドというものがある。
これは直前に実行したコマンドをVimで開いて編集することができるもの。保存して終了すれば編集した内容が即実行される。

https://github.com/Rasukarusan/blog-assets/blob/master/fc-command-ex/fc.gif?raw=true
fcコマンドの挙動。直前のコマンドのみ編集可能

直前のコマンドを編集したい場合だったらfcコマンドでいい。ただ直前のコマンドを編集したいケースはあまりなく、よくあるのはコマンド履歴から遡ってそれを編集といったことが多い。また編集が終わったら即実行されるのもちょっと心臓に悪い。

そういう場合に今回のは使えると思う。

終わり

一時ファイル作るのにmktempを使って、あとで削除(rm)するのはもうちょっとスマートになると思う。たぶん一時ファイル作らずにプロセス置換とかを駆使すればいけるかなあ。
とはいえgit commitとかfcとかと似たような挙動になったのでまあいい。