history-menu - 最近入力したコマンドを現在位置に挿入

最近実行したコマンドをつなげて再実行したい場合みんなならどうする?直前のコマンドならば!!で実行できるね。zshならば!!のあとにTabを押すと実際のコマンドに展開されるし、!!などを使ってコマンドを実行しても展開された形で履歴に登録される。さすがzshだ。
2つ前のコマンドも!-2で展開される。けれど、打ちにくい上Tabも入れると4ストロークと多いので俺は好きではない。

そこで俺はirbsh風のヒストリメニューwidgetを作ってみた。C-vで過去20個の履歴が表示され、asdf〜とホームポジションのキーで選択するとカーソル位置に挿入される。これなら2ストロークでなおかつ履歴が一覧できるので効率的かつ直観的だ。

C-vはquoted-insertでEmacsならC-qなのでC-qに移しておいた。

[*]の位置でC-v s C-v a Enterを押したところの様子はこんな感じだ。zshは複数行にわたるコマンドを編集できるので、履歴の後に改行を入れておいた。これで長ったらしくならずに見やすくなる。

スクリプトに含まれる\021はC-qなのでquoted-insertをデフォルトのC-vで使っている人は\026に書き換えればいいです。

% echo bake
bake
% echo boke
boke
% [*]echo bake
echo boke
bake
boke

ソースは以下だ。もってけ!

HISTORY_MENU_LENGTH=20
typeset -A HISTORY_MENU_KEYS
set -A HISTORY_MENU_KEYS a 1 s 2 d 3 f 4 g 5 h 6 j 7 k 8 l 9 q 10 \
                w 11 e 12 r 13 t 14 y 15 u 16 i 17 o 18 p 19 @ 20
history-menu () {
    zle -M  "`history -n -$HISTORY_MENU_LENGTH  | tac | \
      ruby -e '%w[a s d f g h j k l q w e r t y u i o p @].zip(ARGF.readlines){|k,l| print %[#{k}: #{l}]}' `"
    zle -R
    read -k key
    if [ -n "${HISTORY_MENU_KEYS[$key]}" ]; then
        zle -U "`history -n -${HISTORY_MENU_KEYS[$key]} | head -1 | perl -pe 's/\\\\n/\\021\\n/g'``echo \"\021\"`
"
    else
        zle -R -c
    fi
}

zle -N history-menu
bindkey "^V" history-menu
bindkey "^Q" quoted-insert

ほんとなんでもありだ。なんせzsh内でテトリスすら作れてしまうくらいなんだから。