やりたいこと
Zshの複数行プロンプトで、例えばGitのワーキングツリー状態などを出している場合、コマンド実行後は画面に残したくないんです。
Git情報は入力受付中のプロンプトにだけ出ていればよくて、コマンド実行後はGit情報を消して1行だけのプロンプトを画面に残したいわけです。
やりかた
複数行プロンプトを設定する際、あらかじめ1行版のプロンプトを変数に入れておき、それ以外の行と連結する形で$PROMPTを設定しておきます。下記の例では、普段は1行のプロンプトですが、Gitリポジトリ内部にいるときのみ1行目がGit情報・2行目が通常のプロンプトとなるようにしています。
# 1行版のプロンプトの中身を $BASE_PROMPT 変数に入れる
case $TERM in
xterm*)
BASE_PROMPT=$'%{\e]0;[%n@%m:%~]\a%}'$'[%n@%m%{\e[00m%}]%(5~,%-1~/.../%1~,%~)%# '
;;
*)
BASE_PROMPT=$'[%n@%m%{\e[00m%}]%(5~,%-2~/.../%2~,%~)%# '
;;
esac
# Gitが公式に配布している git-prompt.sh を利用
source ~/.zsh/git-prompt.sh
# プロンプト表示前にGitの状態を検知してプロンプトを書き換える
precmd () {
local git_prompt="$(__git_ps1 '%s')"
if [[ -n "$git_prompt" ]]; then
# Gitリポジトリの内部にいるとき、ワーキングツリーの状態によって色を切り替える
local color=34 # blue
if [[ $git_prompt == *'*'* ]]; then
color=31 # red
elif [[ $git_prompt == *'%'* ]]; then
color=33 # yellow
elif [[ $git_prompt == *'+'* ]]; then
color=32 # green
elif [[ $git_prompt == *'$'* ]]; then
color=35 # magenta
fi
PROMPT=$'\e['"$color"$'m(\ue0a0 '"$git_prompt"$')\e[0m\n'"$BASE_PROMPT"
else
PROMPT="$BASE_PROMPT"
fi
}
# git-prompt.sh 用のオプション設定
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM=""
GIT_PS1_DESCRIBE_STYLE="branch"
GIT_PS1_SHOWCOLORHINTS=0
ここまでだけなら、ただの複数行プロンプトです。次が本記事のポイントで、コマンド実行後にプロンプトを1行だけ残す部分です。
# Enter を打った時に実行する関数
function my_accept_line() {
# プロンプトにgit状態も表示されている場合、git状態の表示を消す
if [[ ${#PROMPT} -ne ${#BASE_PROMPT} ]]; then
local saved_prompt=$PROMPT
PROMPT=$BASE_PROMPT
zle reset-prompt
PROMPT=$saved_prompt
fi
zle accept-line
}
zle -N my_accept_line
bindkey '^M' my_accept_line
ZLEの機能を使って、Enterを打った際にプロンプトを書き換えます。一時的にプロンプトを1行版プロンプトにし、 zle reset-prompt
によってプロンプトおよび入力されたコマンドを再描画するのがミソです。なお、再描画後はプロンプトを元の複数行版に戻しています。