動機づけ

chatty はユーザが入力した文字列に応じて端末のタイトルバーに 小うるさいメッセージを表示するソフトウェアです。

という代物があるのだが、zsh 単独でやってみたい

zsh で chatty 動作イメージ

表示領域

本家 chatty は、複雑な処理となる端末エミュレーションを嫌った結果なのか、 小うるさいメッセージはタイトルバーに表示される。

しかし、端末エミュレータをいじっている時にタイトルバーを見ることは稀だと思う。 小うるさい感を出すためには、ユーザが入力している近傍に小うるさいメッセージを出したほうが良いであろう。

zleでは、 コマンド入力行のすぐ下にメッセージを表示するコマンドzle -M "..."が用意されているので、これを使うことにする。

例えば、zsh のマニュアルのZLEの章にあるように下記のようにすると、

zle-isearch-update() { zle -M "Line $HISTNO"; }
zle -N zle-isearch-update
zle-isearch-exit() { zle -M ""; }
zle -N zle-isearch-exit

インクリメンタルサーチ時に下に行数"Line $HISTNO"が表示される。

[cat@localhost /tmp]$ netstat -altup
bck-i-search: net_
Line 284

辞書

辞書化することも考えたが面倒くさくなったのでやめた。 現状 if/elif の連なりというひどく固定的なコードになっている。

コード

で、self-insert, magic-space, backward-delete-char, delete-char-or-list をホックして、 そのときの入力コマンド($BUFFER)を毎度条件判定してメッセージを表示するという方法に至った。

# chatty: 小うるさい端末 のzsh版 
#
# 元ネタ:http://0xcc.net/chatty/
# 
# Usage:
#     autoload chatty-on
#     chatty-on              
#

chatty-on() {
  zle -N self-insert          chatty-self-insert
  zle -N magic-space          chatty-magic-space
  zle -N backward-delete-char chatty-backward-delete-char
  zle -N delete-char-or-list  chatty-delete-char-or-list
  return 0
}
chatty-off() {
  zle -A .self-insert          self-insert
  zle -A .magic-space          magic-space
  zle -A .backward-delete-char backward-delete-char
  zle -A .delete-char-or-list  delete-char-or-list
  return 0
}

chatty-self-insert() {
  zle .$WIDGET "$@"
  chatty-info
}
chatty-magic-space() {
  zle .$WIDGET "$@"
  chatty-info
}
chatty-backward-delete-char() {
  zle .$WIDGET "$@"
  chatty-info
}
chatty-delete-char-or-list() {
  zle .$WIDGET "$@"
  chatty-info
}

chatty-info() {
  if [[ "$BUFFER" =~ "^ *man ?$" ]]; then
    zle -M "google使えよ"
  elif [[ "$BUFFER" =~ "^ *mail ?$" ]]; then
    zle -M "仕事しないでメールばっかり"
  elif [[ "$BUFFER" =~ "^ *more ?$" ]]; then
    zle -M "まだこんなコマンドあったのか"
  elif [[ "$BUFFER" =~ "^ *reboot ?$" ]]; then
    zle -M "面倒なトラブルは再起動で解決"
  elif [[ "$BUFFER" =~ "^ *vi ?$" ]]; then
    zle -M "emacsは嫌いなのね"
  elif [[ "$BUFFER" =~ "^ *vim ?$" ]]; then
    zle -M "お〜れだっけ V.I.M. <http://www.taiyaki.org/misc/vim/>"
  elif [[ "$BUFFER" =~ "^ *sudo ?$" ]]; then
    zle -M "他人のメールでも見るんでしょ〜"
  elif [[ "$BUFFER" =~ "^ *vipw ?$" ]]; then
    zle -M "rootの雑用ご苦労さま"
  elif [[ "$BUFFER" =~ "^ *w3m ?$" ]]; then
    zle -M "仕事中に目立たずネットサーフィンするのだ"
  elif [[ "$BUFFER" =~ "^ *find ?$" ]]; then
    zle -M "ファイルってすぐどっかいっちゃうんだよね"
  elif [[ "$BUFFER" =~ "^ *telnet ?$" ]]; then
    zle -M "余所の計算機に不正侵入しちゃいまーす"
  elif [[ "$BUFFER" =~ "^ *xman ?$" ]]; then
    zle -M "うげ。まだこんなのあったのか"
  elif [[ "$BUFFER" =~ "^ *killall ?$" ]]; then
    zle -M "皆殺し大好き"
  elif [[ "$BUFFER" =~ "^ *./configure ?$" ]]; then
    zle -M "途中でこけたりして"
  elif [[ "$BUFFER" =~ "^ *#jet ?$" ]]; then
    zle -M "jet氏のメッセージ"
  elif [[ "$BUFFER" =~ "^ *make ?$" ]]; then
    zle -M "ソースからビルドかよ"
  elif [[ "$BUFFER" =~ "^ *rm -f ?$" ]]; then
    zle -M "実は大切なファイルだったりして..."
  elif [[ "$BUFFER" =~ "^ *rpm ?$" ]]; then
    zle -M "バイナリで手抜きかよ"
  elif [[ "$BUFFER" =~ "^ *ssh ?$" ]]; then
    zle -M "隠しごとかよ"
  elif [[ "$BUFFER" =~ "^ *pwd ?$" ]]; then
    zle -M "今いる場所ぐらい覚えとけよ"
  elif [[ "$BUFFER" =~ "^ *emacs ?$" ]]; then
    zle -M "やっぱりお前もAの横のCapsLock死ねとか思ってるの?"
  elif [[ "$BUFFER" =~ "^ *gimp ?$" ]]; then
    zle -M "失敗した写真の修正?"
  elif [[ "$BUFFER" =~ "^ *kill ?$" ]]; then
    zle -M "ひでーやつだ"
  elif [[ "$BUFFER" =~ "^ *less ?$" ]]; then
    zle -M "むしろ動体視力鍛えてcatでしょ"
  elif [[ "$BUFFER" =~ "^ *ls ?$" ]]; then
    zle -M "cdの後のそれは癖なんか?"
  else
    # clear
    zle -M ""
  fi
}

# Handle zsh autoloading conventions
chatty-on "$@"

使い方は chatty-on というファイル名で /usr/share/zsh/site-functions (などの$fpathが通っているフォルダ)に置いた上で、下記を実行する。

% autoload chatty-on
% chatty-on

ただし、お試しとして使うために $fpathが通っているフォルダに置きたくない場合は、 下記でもよい。

% source /path/to/chatty-on
% chatty-on

参考文献