続・zshからanything.elをシームレスに利用して履歴検索
今週は寒いようで、一気に気温が一桁になりましたね。でもコートは着ない私・・・すみません嘘です、コート持ってないんです。
防寒着繋がりですが、道を歩いているとよくマフラーをしている人を見かけますね。私は雪国出身ではありますが、生まれてこのかた数回しかマフラーしたことありません。
何かが首に触れているのがすんごい嫌なためですが、それでもちゃんとした服装をしていれば寒くないものです。多分。
さて、以前zshから無理矢理anything.elの機能を利用して履歴検索を高速にすることに成功しましたが、あれには何個か問題がありました。
- 日本語が混じるような履歴を扱うと止まる
- 毎回履歴全件から検索するため、履歴が多い環境だと遅延が発生する
- なんかコードがイケてない
最後はとっても個人的なものですのでどうでもいいですが、特に最初のが致命的です。滅多に履歴に日本語が入ってくることは無いと思いますが、たまさか入ってくると、その時点でEmacs側の処理が停止するため、非常にストレスフルな環境に早変りしてしまいます。
で、上二つを合わせて、以下のように修正してみました。
- 日本語が入っていても停止しないように
- 毎回履歴を全件検索するが、本当に全件ではなく見付けた順に取得するように変更
- 処理がちょいとダサいですが。
- ちゃんとzshでzleを起動した時点で、バッファの文字列で検索が行われて結果を表示するように
- 最初からやっておくべきでした、はい。
とりあえず以下がelispと.zshrcのソースです。必要なものは変わりません。
Emacs lisp (require 'anything-complete) (defvar azhzle/cache nil) (defvar azhzle/tmp-file "/tmp/.azh-tmp-file") (defvar azhzle/dicision-keys '("A" "S" "D" "F" "G" "H" "J" "K" "L" "Q" "W" "E" "R" "T" "Y" "U" "I" "O" "P")) (defun anything-zsh-history-from-zle-init () (let ((anything-sources '(anything-c-source-complete-shell-history))) (anything-initialize) (unless azhzle/history-list (setq azhzle/history-list (azhzle/get-history-list))) (setq azhzle/cache azhzle/history-list)) ) (defun azhzle/input (str) (interactive) (let* ((lists (azhzle/get-history-line str))) (with-temp-buffer (loop for i from 1 upto (if (<= (length lists) (length azhzle/dicision-keys)) (length lists) (length azhzle/dicision-keys)) for line in lists do (insert (nth (1- i) azhzle/dicision-keys) ":" (substring-no-properties (car line)) "\n")) (set-buffer-file-coding-system 'raw-text-unix t) (write-region (point-min) (point-max) azhzle/tmp-file)) (setq azhzle/cache lists) )) (defun azhzle/get-history-line (pattern) (with-current-buffer (shell-history-buffer) (let ((lists '()) (limit (length azhzle/dicision-keys)) (matches '()) (exit nil) (anything-pattern pattern) (zsh-p t)) (loop while (or (not (bobp)) exit) initially (goto-char (point-max)) if (or (bobp) exit (< limit (length matches))) do (return) else do (loop for i from 1 upto limit if (or (bobp) (= (point-at-bol) (point-at-eol))) do (previous-line) (return) else do (push (list (acsh-get-line (line-beginning-position) (line-end-position))) lists) (previous-line)) (setq matches (append matches (anything-compute-matches `(,@anything-c-source-complete-shell-history ,(cons 'candidates 'lists) (volatile))))) (setq lists nil)) matches))) (defun azhzle/dicision-history (history-num) (interactive) (with-temp-buffer (erase-buffer) (insert (substring-no-properties (car (nth (- history-num 1) azhzle/cache)))) (write-region (point-min) (point-max) azhzle/tmp-file) ))
## 各固定値 typeset -A HISTORY_DICISION_KEYS set -A HISTORY_DICISION_KEYS A 1 S 2 D 3 F 4 G 5 H 6 J 7 K 8 L 9 Q 10 \ W 11 E 12 R 13 T 14 Y 15 U 16 I 17 O 18 P 19 Z 20 X 21 C 22 V 23 B 24 N 25 M 26 ISHR_MENU_LENGTH=26 ISHR_FILENAME="/tmp/.azh-tmp-file" function ishr-search-history-from-anything() { emulate -L zsh local key=$1 # 特定キーが押されたら、該当する位置の履歴をバッファに表示する。 if [[ -n "${HISTORY_DICISION_KEYS[$key]}" && -n "$BUFFER" ]]; then zle -A .self-insert self-insert emacsclient --eval "(azhzle/dicision-history ${HISTORY_DICISION_KEYS[$key]})" BUFFER=`cat $ISHR_FILENAME` zle -R -c zle accept-line return 1 fi return 0 } function ishr-update-status() { emacsclient --eval "(azhzle/input \"$BUFFER\")" &> /dev/null zle -M "`cat $ISHR_FILENAME`" zle -R } function ishr-self-insert() { emulate -L zsh LBUFFER+=${KEYS[-1]} ishr-search-history-from-anything ${KEYS[-1]} (( ! $? )) && ishr-update-status } function ishr-backward-delete-char() { emulate -L zsh zle .backward-delete-char ishr-update-status } ####################################### # incremental-search-history-menu本体 # ####################################### zle -N incremental-search-history-menu function incremental-search-history-menu() { # インクリメンタル履歴検索を行えるように準備等を行う。 emulate -L zsh integer stat # 各種必要な変数の初期化。 emacsclient --eval "(anything-zsh-history-from-zle-init)" &> /dev/null ishr-update-status zle -N self-insert ishr-self-insert zle -N backward-delete-char ishr-backward-delete-char zle recursive-edit stat=$? zle -A .self-insert self-insert zle -A .backward-delete-char backward-delete-char # 各種終了処理 rm -f $ISHR_FILENAME zle -R -c (( stat )) && zle send-break return $? } bindkey "^[@" incremental-search-history-menu
bindkeyは好みでどうぞ。
個人的には、これでなんとか常用できそうです。一番不安なのはこんなanything.elの使いかたしていーんだろうかということ。まぁいいか(ぁ