
今使っている zsh の右プロンプトにはカレントディレクトリを表示しているのですが、 Git や Mercurial のワークツリー(ワーキングディレクトリ)の中にいるときは、そのルートディレクトリに下線を引くようにしています。
これがなかなか気に入っているので紹介します。
以下では主要な部分だけ抜き出して解説しています。 zsh 設定ファイルの全体は github にアップしています。https://github.com/kyanagi/dot.zsh.d
まず、vcs_info の設定で formats に %r を設定しておき、リポジトリ名を取得できるようにします。 次の例だと、$vcs_info_msg_2_ で取得できるようになります。 http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#Configuration
zstyle ':vcs_info:*' formats '%F{white}%c%u%s%f%F{white}:%f%F{green}__branch-name__%f' '%b' '%r'
次に、RPROMPT の設定です。
local rprompt
rprompt=(
"%F{$prompt_color}["
"\${vcs_info_msg_0_:+\${vcs_info_msg_0_} }%f" # VCS の情報があれば表示
"%F{$prompt_color}"
# $PWD は ~ が展開されているので ~ に戻し、リポジトリ名の部分に下線を付ける。
# リポジトリ名は $vcs_info_msg_2_ に入っている。
"\${\${PWD/#\$HOME/~}/\${vcs_info_msg_2_}/%U\${vcs_info_msg_2_}%u}"
" %*]%f"
)
RPROMPT=${(j..)rprompt}
rprompt に配列を作ってから連結しているのはコメントを書くためです。 (j.. については http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags を参照)
いろいろと書いていますが、今回の記事に関係するのは
"\${\${PWD/#\$HOME/~}/\${vcs_info_msg_2_}/%U\${vcs_info_msg_2_}%u}"
の部分です。
以下ではこの内容をもう少し詳しく説明します。
右プロンプトに $PWD の値を出したいとします。
RPROMPT="$PWD"
とすると、(このコマンドを入力したときのカレントディレクトリが /var/tmp として)実際には
RPROMPT=/var/tmp
と展開されて実行されるので、その後ディレクトリを移動しても表示は常に /var/tmp のままになります。
右プロンプトの表示ごとにその瞬間の $PWD を表示するには、RPROMPT に「$PWD」という文字列を設定する必要があるので
RPROMPT="\$PWD"
としなくてはいけません。
以上をふまえた上で、実際に右プロンプトの設定内容を見てみます。
~/tmp に tdiary-core をチェックアウトし、tdiary-core/spec/core に移動した状況を考えます。
各変数の内容は以下のような感じです。
| 変数 | 内容 |
|---|---|
| $PWD | /Users/ani/tmp/tdiary-core/spec/core |
| $HOME | /Users/ani |
| $vcs_info_msg_2_ | tdiary-core |
まず、$PWD は ~ が展開されているので ~ に戻します。
| ${PWD/#$HOME/~} | ~/tmp/tdiary-core/spec/core |
(${name/pattern/repl} は name の pattern を repl に置換して展開するという意味です。# で始まるパターンは文字列の先頭を意味します。 http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion)
次に、リポジトリ名 tdiary-core を下線付きにします。 リポジトリ名は $vcs_info_msg_2_ に入っています。 プロンプトで下線を引くには %U ... %u で囲います。
よって、$vcs_info_msg_2_ を %U${vcs_info_msg_2_}%u で置換します。
| ${${PWD/#$HOME/~}/${vcs_info_msg_2_}/%U${vcs_info_msg_2_}%u} | ~/tmp/%Utdiary-core%u/spec/core |
最後に、これを右プロンプトの表示ごとに解釈するため、$ をエスケープします。
\${\${PWD/#\$HOME/~}/\${vcs_info_msg_2_}/%U\${vcs_info_msg_2_}%u}
これで当初の設定どおりとなります。
なお、リポジトリ名と同じ名前のディレクトリがワークツリーより上にあった場合、そちらの方に下線が引かれてしまいます。 しかしこれはレアケースだと思いましたので、簡単のため特に対応していません。 (真面目にやるなら vcs_info formats の %R でフルパスが取れるので、それを使えば対処できると思います)
RPROMPT の内容は以下のように修正しました。
"${${${PWD/#\$HOME/~}/\/${vcs_info_msg_2_}\///%U${vcs_info_msg_2_}%u/}/%\/${vcs_info_msg_2_}//%U\${vcs_info_msg_2_}%u}"
リポジトリ名が foo であるとき、カレントディレクトリが /foobar/foo だと foobar の foo に下線が引かれていたので、 下線はディレクトリ全体に対して置換するように修正しました。
読みづらいですが、リポジトリ名を foo とすると、まず /foo/ を /%Ufoo%u/ に置換し、 次に文字列末尾の /foo を /%Ufoo%u に置換しています。
zsh を使っている方には、次のような設定を入れている方も多いと思います。
function cdup() {
echo
cd ..
zle reset-prompt
}
zle -N cdup
bindkey '\^' cdup
これは ^ で上のディレクトリに移動できるようにする設定です。
普通に ^ を入力したいときは C-v ^ と入力するのですが、 テキストをコピペでターミナルに貼り付けたとき、テキストに ^ が混ざっていて意図しない結果になることが何度かありました。
この問題を改善するため、次のように変更しました。
function cdup-or-insert-circumflex() {
if [[ -z "$BUFFER" ]]; then
echo
cd ..
zle reset-prompt
else
zle self-insert '^'
fi
}
zle -N cdup-or-insert-circumflex
bindkey '\^' cdup-or-insert-circumflex
これで、入力がないときは上のディレクトリに移動し、それ以外の場合は ^ が入力されるようになります。
scratch バッファの便利な設定をまとめた scratch-ext.el を作りました。
このファイルを読み込むと
といったことができるようになります。
github で公開していますので、どうぞご利用ください。
https://github.com/kyanagi/scratch-ext-el
*scratch* バッファを消さないようにする:長年利用していた設定です。バッファの自動保存を仕込むのを機に、同様の機能を再実装することにしました。
これまでEmacsではanythingを使っていなかったのですが、 ふと思い立ってanythingを入れてみることにしました。 (正確に言うと何度か手を出してみたことはあったがすぐやめてしまった)
まだ触り始めたばっかりですが、とまどった点などを中心に書いておきます。
現在の設定は以下のとおり。
入力を修正しようと思って C-h を連打すると大変なことになってしまうので、 C-h は backward-kill-word に割り当て直しました。
最初 anything-map だけに設定していて、思い通りの動作にならずに迷いました。
ついでに C-w で単語削除できるようにもしてあります。
popwinと組み合わせようとしましたが、ミニバッファの履歴検索が動かなくなったので外しました。 このへんはきちんと調べていないので、ちゃんとやれば動くかもしれません。
anything ウィンドウでバッファを選ぶときは C-z を押さないと中身を見ることができません。
これまで使っていた iswitchb では、選択を確定する前に選択中のバッファを表示するようにしていたので、 この機能はぜひ欲しいと思っていました。
試してみた結果、anything-move-selection-after-hook で表示してうまくいってます。
あとここには書いてないですが、auto-install-compatibility-setup が anything を読み込むというのに気付かなくて ちょっとはまってました。まあもういらないかなとも思ったので auto-install-compatibility-setup は外しました。
zsh には vcs-info というものがあり、これを利用すると Git や Mercurial のブランチ名などをプロンプトに表示することができます。 (例: http://d.hatena.ne.jp/mollifier/20090814/p1)
しかし使ってみるとわかるのですが、Cygwin だと結構遅いです。 シェルのプロンプトが表示されるたびにちょくちょく待たされる感じになってしまい、かなりストレスを感じます。
どうにかならないものかと思っていたのですが、ひとまず実用上問題なさそうなところまで高速化できたので共有しておきます。 (私が Mercurial メインで使っているので、Mercurial を使う際の高速化が中心です。Git だとまだ改善の余地が多いかと思います)
vcs_info はたくさんのバージョン管理システムに対応していますが、 現在のディレクトリがそれぞれのシステムに対応しているか調べるため、 多くのシステムを有効にしていると重くなってしまいます。
デフォルトでは全部有効になっているので、使うものだけを指定するようにします。
zstyle ':vcs_info:*' enable hg git
ここでは Mercurial と Git を指定しています。 ここに書かれた順にチェックがされるので、Mercurial の作業コピーで作業することが多い人は、 このように hg を先に書いた方がレスポンスが速くなります。
調べてみたところ、Cygwin だとコマンド置換 $(...) が遅いのが一番のネックになっているようです。 例えば私の環境では
zsh -fc 'repeat 100 $()'
が、Linux(Debian) では 0.04 秒ほどで終了するのに対し、Cygwin では 4 秒弱かかりました。
そこで、なるべくコマンド置換をしないように修正します。
--- /usr/share/zsh/4.3.12/functions/VCS_INFO_bydir_detect 2011-08-08 04:59:41.001000000 +0900
+++ ./VCS_INFO_bydir_detect 2012-01-19 21:52:35.293434600 +0900
@@ -6,7 +6,7 @@
local dirname=$1
local basedir="." realbasedir file
-realbasedir="$(VCS_INFO_realpath ${basedir})"
+realbasedir="${basedir:A}"
while [[ ${realbasedir} != '/' ]]; do
[[ -r ${realbasedir} ]] || return 1
if [[ -n ${vcs_comm[detect_need_file]} ]] ; then
@@ -20,7 +20,7 @@
fi
basedir=${basedir}/..
- realbasedir="$(VCS_INFO_realpath ${basedir})"
+ realbasedir="${basedir:A}"
done
[[ ${realbasedir} == "/" ]] && return 1
現在のディレクトリが Mercurial 管理下にあるかどうかを調べるため、 .hg ディレクトリがあるかどうかを現在のディレクトリからルートディレクトリまで順に探しています。
../ 混じりのパスを実際のパスに直すため VCS_INFO_realpath という関数を使っているのですが、 これは変数展開の A 修飾子でできるのでそちらを使います。
--- /usr/share/zsh/4.3.12/functions/VCS_INFO_formats 2011-08-08 04:59:41.001000000 +0900
+++ ./VCS_INFO_formats 2012-01-20 02:42:04.744074700 +0900
@@ -29,7 +29,16 @@
)
hook_com[base-name]="${${hook_com[base]}:t}"
hook_com[base-name_orig]="${hook_com[base_name]}"
-hook_com[subdir]="$(VCS_INFO_reposub ${hook_com[base]})"
+
+# コマンド置換が遅い Cygwin で高速化するために VCS_INFO_reposub を展開
+#hook_com[subdir]="$(VCS_INFO_reposub ${hook_com[base]})"
+local base=${hook_com[base]%%/##}
+if [[ ${PWD} == ${base}/* ]]; then
+ hook_com[subdir]=${PWD#$base/}
+else
+ hook_com[subdir]='.'
+fi
+
hook_com[subdir_orig]="${hook_com[subdir]}"
VCS_INFO_hook 'post-backend'
VCS_INFO_reposub はここでしか使われていなかったので、中身を展開してしまいました。
Mercurial だけを使うならここまででいいのですが、とりあえず Git に対しても vcs-info を使うようにしていたところ、 特にバージョン管理下にないディレクトリにいるときに重い状態でした。
これは、バージョン管理下にないディレクトリに対しては、Mercurial のワーキングコピーであるかどうかのチェックと Git のワーキングコピーであるかどうかのチェックが両方走っていて、Git のチェックが重かったのが原因でした。
よって次の修正を加えています。
--- /usr/share/zsh/4.3.12/functions/VCS_INFO_detect_git 2011-08-08 04:59:46.001000000 +0900
+++ ./VCS_INFO_detect_git 2012-01-23 17:40:41.272189100 +0900
@@ -6,8 +6,10 @@
[[ $1 == '--flavours' ]] && { print -l git-p4 git-svn; return 0 }
-if VCS_INFO_check_com ${vcs_comm[cmd]} && ${vcs_comm[cmd]} rev-parse --is-inside-work-tree &> /dev/null ; then
- vcs_comm[gitdir]="$(${vcs_comm[cmd]} rev-parse --git-dir 2> /dev/null)" || return 1
+if VCS_INFO_check_com ${vcs_comm[cmd]} ; then
+ local gitdir
+ ${vcs_comm[cmd]} rev-parse --git-dir 2> /dev/null | read gitdir || return 1
+ vcs_comm[gitdir]=$gitdir
if [[ -d ${vcs_comm[gitdir]}/svn ]] ; then vcs_comm[overwrite_name]='git-svn'
elif [[ -d ${vcs_comm[gitdir]}/refs/remotes/p4 ]] ; then vcs_comm[overwrite_name]='git-p4' ; fi
return 0
現在のディレクトリが Git の管理下にあるかどうかを調べているのですが、 git rev-parse --git-dir の戻り値をチェックすれば git rev-parse --is-inside-work-tree は不要だと思うので前者だけにしました。 コマンド置換が read になっているのは、ほぼ気分の問題です。(微妙に速くなってる気もしますが、大して変わりません)
use-simple true の設定にするときは hexdump を忘れずに入れるようにしましょう。これがないと速くなりません。util-linux パッケージにあります。 (ただし、check-for-changes を有効にするときは use-simple false でないといけません)
Cygwin 上で zsh の vcs_info の高速化を試みました。
この修正を加えたスクリプトは https://github.com/kyanagi/faster-vcs-info にあります。(zsh 4.3.12 のスクリプトを修正したものです)
使う際は適当なディレクトリに置いて、そのディレクトリを$fpath の頭の方(システムの vcs_info より優先されるように)に加えてください。
普段 Windows ではエディタに Emacs を使っていますが、これまで、intlfonts が好みだったため、Emacs22 を使い続けていました。 (Windows の Emacs23 は BDF フォントが使えないようなので)
しかし、さすがに移行するべきかなということで、思い切って Emacs23.3 にアップデートしてみました。
フォントは、MeiryoKe_Gothic と Inconsolata の組み合わせがなかなかいい感じだったので、以下のように設定しました。
(set-face-attribute 'default nil :family "Inconsolata" :height 150)
(let ((fn (frame-parameter nil 'font)))
(dolist (target '(katakana-jisx0201
japanese-jisx0208
japanese-jisx0212
japanese-jisx0213.2004-1
))
(set-fontset-font fn target '("MeiryoKe_Gothic" . "iso10646-1"))))
(setq face-font-rescale-alist
'((".*MeiryoKe_Gothic.*" . 0.89)
("-cdac$" . 1.3)))
(setq-default line-spacing 2)
(setq scalable-fonts-allowed t)
(setq w32-enable-synthesized-fonts t)
メイリオの大きさは、英数字の行に日本語が混ざっても高さが変化しないぎりぎりで設定してあります。全角文字と半角文字の幅は2:1にはなっていませんが、高さが変わらないことの方を優先しました。
Padrino 0.9.15 と ActiveRecord 3.0.0 の組み合わせで、migration の add_index でエラーが発生しました。
% padrino rake ar:migrate:reset ...(略) undefined method `key?' for nil:NilClass /usr/local/lib/ruby/gems/1.8/gems/padrino-core-0.9.15/lib/padrino-core/cli/rake.rb:9:in `init' (See full trace by running task with --trace)
調べてみると、add_index の第3引数を書かないと padrino は options に nil を渡すのですが、ActiveRecord 側で nil を渡されると動かないようになっていました。 ひとまず、以下のコードを lib/ar_add_index_fix.rb に置いて対処しました。
module ActiveRecord::ConnectionAdapters::SchemaStatements
alias orig_add_index add_index
def add_index(table_name, column_name, options = {})
orig_add_index(table_name, column_name, options || {})
end
end