zshの履歴検索 with anything
なんかやたらと疲れが溜まっています。主に目に。現場の環境がよくないんでしょうか。
かなり前に、zshからanythingを裏方で呼び出して、表面上はシームレスにanythingの履歴確認ができるようにしていました。
これです。
自分で作って、かなり長いこと使っていたのですが、ずっと不満だったことが一点ありました。
それは、anything-match-pluginで提供されている、空白区切りによる複数候補の検索ができなかったことでした。やろうやろうと思っていましたが、年末年始ということもあってすっかり忘れてしまっておりました。
ふと思いだしたので、anythingのソースとにらめっこをしながら、以前のソースを書き換えてみました。
以前は、anythingが内部で呼び出していたanything-compute-matchesという関数を直接呼び出し、毎回自分で行の取得とかまでわざわざやっていました。
ところで、anything + anything-match-pluginはどのようにして、複数候補によるマッチングを実現させているのか、私はよくわかりません。というわけでソースを覗いてみると、
(defun anything-compile-source--match-plugin (source) (let ((searchers (if (assoc 'search-from-end source) anything-default-search-backward-functions anything-default-search-functions))) `(,(if (or (assoc 'candidates-in-buffer source) (equal '(identity) (assoc-default 'match source))) '(match identity) `(match ,@anything-default-match-functions ,@(assoc-default 'match source))) (search ,@searchers ,@(assoc-default 'search source)) ,@source)))
こんな関数があって、この中で渡されたsourceに(search ,@searchers ,@(assoc-default 'search source))というのを追加して返しています。今回ソースを読んだことで、anythingのプラグイン機構が理解できるという副作用までありましたが、それは置いておいて。
つまりは、この,@searchersを利用するanything.elの関数を探せばええやろう、ということで探したところ、「anything-candidates-in-buffer」という関数が見付かりました。これも直接起動するようなものではなく、組み込みのanythingプラグインでしたが。
なにはともあれ、これを呼び出せば、anything-match-pluginの機能を利用してくれるということがわかったので、これを使って書き換えました。
;; zshからanythingを利用するための設定。 (require 'anything-complete) (defvar azhzle/tmp-file "/tmp/.azh-tmp-file") (defvar azhzle/cache nil) (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 ,(cons 'candidates 'lists) (volatile))))) (anything-initialize))) (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 (if (listp line) (car line) 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* ((anything-pattern pattern) (anything-sources `((,@anything-c-source-complete-shell-history ,(cons 'candidates 'lists) (volatile)))) (source (car (anything-get-sources))) (anything-source-name (cdr (assoc 'name source))) (zsh-p t)) (anything-candidates-in-buffer)))) (defun azhzle/dicision-history (history-num) (interactive) (with-temp-buffer (erase-buffer) (insert (substring-no-properties (nth (- history-num 1) azhzle/cache))) (write-region (point-min) (point-max) azhzle/tmp-file) ))
これが全部です。anything-candidates-in-bufferを利用するようにしたことで、かなりの行数削減ができました。かなりすっきりです。ちなみにzshrcへの記述内容は以前と変わっていません。
どうでもいいんですが、Emacserの方々はzshを {terminal, Emacs}のどちらから利用しているんでしょうか?私はmlterm + tmuxで利用しています。ansi-termとかshell-modeから利用したこともありますが、どうにも面倒な上、ログが溜まると重くなる一方だった記憶しかありません。
とりあえずこれで、より自前で利用するのが便利になりました。次は任意のsourceを利用するようにしてみたいんですが、zsh上でわざわざsourceを利用する意味がよくわかりません(ぉ