ZshでOSC 133に対応する

OSC 133という仕様があります。 これはシェルやREPLでプロンプトと実行結果の出力を一まとまり単位として扱うためのマークを決まったエスケープシーケンスで行うことで、ターミナルエミュレーターが解釈できるようにしようという提案です。

Bashでもやりたいけど、bashには preexecprecmd がないので無理かなあと半分諦めていた。 が、ふとした時にやり方を発見したので早速やってみた。

screenshot of iTerm2

iTerm2 では、以下のようなことができる。

  • Toolbelt -> Show Toolbelt (⌘ + Shift + b) の Command History に実行コマンドの履歴が出る(上図)
  • ⌘ + Shift + ↑ (Edit -> Marks and Annotations -> Next Mark) で前のコマンド実行行へジャンプ
  • ⌘ + shift + ↓ (Edit -> Marks and Annotations -> Previous Mark) で次のコマンド実行行へジャンプ
  • ⌘ + Shift + a (Edit -> Select Output of Last Command) で直前コマンドの実行結果を選択
# See: https://github.com/rcaloras/bash-preexec
source ~/var/bash-preexec/bash-preexec.sh

_prompt_executing=""
function precmd() {
	local ret="$?"
	if test "$_prompt_executing" != "0"
	then
		_PROMPT_SAVE_PS1="$PS1"
		_PROMPT_SAVE_PS2="$PS2"
		PS1="\e]133;P;k=i\a"$PS1"\e]133;B\a\e]122;> \a"
		PS2="\e]133;P;k=s\a"$PS2"\e]133;B\a"
	fi
	if test "$_prompt_executing" != ""
	then
		# FTCS_COMMAND_FINISHED
		printf "\033]133;D;%s;aid=%s\007" "$ret" "$$"
	fi
	# FTCS_COMMAND_START
	printf "\033]133;A;cl=m;aid=%s\007" "$$"
	# コマンド実行中Flag -> off
	_prompt_executing=0
}
# Enterを押してコマンド実行直前
function preexec() {
	PS1="$_PROMPT_SAVE_PS1"
	PS2="$_PROMPT_SAVE_PS2"
	# FTCS_COMMAND_EXECUTED
	printf "\033]133;C;\007"
	# コマンド実行中Flag -> on
	_prompt_executing=1
}

eval "$(starship init bash)"

bash-preexec.sh の発見

Starship でプロンプト設定をしているが、eval $(starship init bash) の中身ってどうなっているの? という疑問が湧いて、 /usr/local/bin/starship init bash --print-full-init を実行してななめ読みしたところ、 bash-preexec を発見。

(略)
# If the user appears to be using https://github.com/rcaloras/bash-preexec,
# then hook our functions into their framework.
if [[ "${__bp_imported:-}" == "defined" || $preexec_functions || $precmd_functions ]]; then
    # bash-preexec needs a single function--wrap the args into a closure and pass
    starship_preexec_all(){ starship_preexec "$_"; }
    preexec_functions+=(starship_preexec_all)
    precmd_functions+=(starship_precmd)
else
(略)

これぞ求めていたもの。

Bash用にちょい修正

ZshでOSC 133に対応する の zsh設定をほぼ丸パクリしつつ、bash用に書いたのが上のコード