s9g

vcs_info の使い方 あるいは prompt_subst のススメ

みなさんバージョンコントロールしてますか? 人生もバージョンコントロールしたいですね.

zsh 上でバージョンコントロールを扱う場合に便利な vcs_info の使い方です. しかしながら自分で自分の使う VCS を使ってがりがりスクリプト書いたほうがいいような気もしますが気のせいです.
以下のようなものを $ZDOTDIR/.zshrc に書くだけです PROMPT のあたりは各自調整のこと.

setopt prompt_subst
autoload -Uz add-zsh-hook
autoload -Uz vcs_info
zstyle ':vcs_info:*' formats '%s' '%b' '%i' '%c' '%u'
zstyle ':vcs_info:*' actionformats '%s' '%b' '%i' '%c' '%u' '%a'
zstyle ':vcs_info:*' get-revision true
zstyle ':vcs_info:*' check-for-changes true
zstyle ':vcs_info:*' max-exports 6
function _precmd_vcs_info () {
  LANG=en_US.UTF-8 vcs_info
}
add-zsh-hook precmd _precmd_vcs_info

PROMPT+="\$(
if [[ -n \"\${vcs_info_msg_0_}\" ]]; then
  echo -n \" [\${vcs_info_msg_0_}:\"
  if [[ \"\${vcs_info_msg_0_}\" == \"git\" && -z \"\${vcs_info_msg_2_}\" ]]; then
    rev=\$(git rev-parse --short HEAD)
    namerev=\$(git name-rev --name-only --no-undefined \$rev)
    if [[ -n \"\$namerev\" ]]; then
      echo -n \"\$rev(\$namerev)\"
    else
      echo -n \"\${vcs_info_msg_1_}\"
    fi
  else
    echo -n \"\${vcs_info_msg_1_}\"
  fi
  if [[ -n \"\${vcs_info_msg_3_}\" || -n \"\${vcs_info_msg_4_}\" ]]; then
    echo -n \":\${vcs_info_msg_3_}\${vcs_info_msg_4_}\"
  fi
  if [[ -n \"\${vcs_info_msg_5_}\" ]]; then
    echo -n \":\${vcs_info_msg_5_}\"
  fi
  echo -n \"]\"
fi
)%E
"
fi
setopt prompt_subst
ちょーべんりなプロンプトを実現します. 具体的には PROMPT 変数の中の変数参照をプロンプト表示時に展開します. ここで $() によるシェルスクリプト埋め込みを用いるとなんでもできちゃうという寸法です.
autoload -Uz add-zsh-hook
いろんなフックを便利にかけることができる関数をロードします. これなしだと何の前置きもない変数を用意して代入すると…みたいな感じになって見通しよくない気がします. が中ではそういうことやってるよね.
autoload -Uz vcs_info
キモです. vcs_info 関数をロードします. 中ではいろいろやってます. git の場合 pid が 30 程度進むくらいにいろいろやってます.
zstyle ...
vcs_info のカスタマイズなんかをやってます. だいたい使うところはこんなところじゃないでしょうか. 細かいところは man zshcontrib してください.
function _precmd_vcs_info
名前はどうでもいいです. precmd フックで vcs_info を呼ぶだけの簡単なお仕事です. vcs_info を呼ぶ際の locale をうまくしておかないとうまくいかない場合があるようです*1.
add-zsh-hook precmd _precmd_vcs_info
先の関数を precmd フックにひっかけます. うまいことしてくれます.
PROMPT+=...
vcs_info が呼ばれると vcs_info_msg_{n}_ という変数にいろいろ入ってくるので美味しく調理しましょう. ここでは prompt_subst によってべんりになったプロンプト機能によってシェルコマンドをその場で展開してもらいます. ここではエスケープに注意しましょう. *2 psvar という変数に入れてプロンプト側で%vで受け取る方法もありますが趣味の問題です. psvar は配列となっていて私には扱いにくかったのです. こっちの方が直感的に扱えますし.
git rev-parse --short HEAD; git name-rev --name-only --no-undefined $rev
どうも vcs_info は git の no branch 状態をうまく扱えていないようなので微妙に調整を施します. どうでもいいですよね.

バージョンチェック

vcs_infozsh 4.3.7 以上に同梱です. それ未満の環境でも同じ zshrc を使う場合はバージョンチェックが必要になるでしょう. 以下ではその方法を記しておきます.

autoload -Uz is-at-least

if is-at-least 4.3.7; then
  ... ここに 4.3.7 以上向けのコード ...
fi
autoload -Uz is-at-least
zsh のバージョンチェック関数をロードします. 下からしかできません.
if is-at-least 4.3.7
こう使うみたいです. 直感的!

あとがき

vcs_info とか prompt_subst というか add-zsh-hook とか is-at-least の紹介になってしまった気がいたしますが, もうどうでもいいや. vcs_info はいろんなところで紹介されてるし. でも各所でフックのかけかたとかバージョンチェックとかがなってない印象だったのと, psvar 使って見通し悪そうに見えたのでこういう方法もありますよ, くらいのつもりで. prompt_subst は強力ではあるけれど precmd + psvar + %v で代用できなくはなかったり, エスケープ考えなきゃだったりでアレなところはありますが, psvar の添字考えるのとどっちがどーかみたいなところはありますしね.

*1:vcs_info で Subversion のリビジョン番号が出ない問題 - 永遠に未完成

*2:EUC-JP の制限 (?) で円記号になっていますが U+005C です. あー webkitEUC-JP と U+005C のアレかー….