anything.elを使ってzshの履歴検索をする

zshは快適なシェルではあるが、anything脳の俺にとっては履歴検索で絞り込めないと不便でしょうがない。
zshでanythingのようなものを作る方法があるものの、やはり本物のanything.elを使いたい。
ということでzshコマンドラインからanythingで履歴検索するコードを書いてみた。

更新情報

  • [2009/12/10]id:IMAKADOからのCarbonEmacs用のパッチを取り込み。

インストール

とりあえず、anything一式が必要なのでインストール。

M-x install-elisp-from-emacswiki anything.el
M-x install-elisp-from-emacswiki anything-config.el
M-x install-elisp-from-emacswiki anything-match-plugin.el
M-x install-elisp-from-emacswiki anything-complete.el

そして、Emacs Lispコード。(将来的には名前をつけるつもり)

;; (save-window-excursion (shell-command (format "emacs -Q -L ~/emacs/lisp -l test-minimum -l %s %s &" buffer-file-name buffer-file-name)))
(require 'anything-complete)
(defun anything-zsh-history-from-zle ()
  (interactive)
  (azh/set-frame)
  (let ((anything-samewindow t)
        (anything-display-function 'anything-default-display-buffer))
    (azh/set-command
     (anything-other-buffer
      `(((name . "History")
         (action
          ("Paste" . identity)
          ("Edit" . azh/edit-command))
         ,@anything-c-source-complete-shell-history))
      "*anything zsh history*"))))

(defvar azh/tmp-file "/tmp/.azh-tmp-file")
(defvar azh/frame nil)

(defun azh/set-frame ()
  (unless (and azh/frame (frame-live-p azh/frame))
    (setq azh/frame (make-frame '((name . "zsh history")
                                  (title . "zsh history")))))
  (select-frame azh/frame)
  (sit-for 0))
;; (progn (azh/set-frame) (anything))
(defun azh/set-command (line)
  (write-region (or line "") nil azh/tmp-file)
  (azh/close-frame))

(defun azh/close-frame ()
  (ignore-errors (make-frame-invisible azh/frame))
  (when (fboundp 'do-applescript)
    (funcall 'do-applescript "tell application \"iTerm\"
                                activate
                             end")))

(defun azh/edit-command (line)
  (switch-to-buffer "*zsh command edit*")
  (erase-buffer)
  (setq buffer-undo-list nil)
  (azh/edit-mode)
  (insert line)
  (recursive-edit)
  (buffer-string))

(define-derived-mode azh/edit-mode fundamental-mode
  "Press C-c C-c to exit!"
  "Edit zsh command line"
  (define-key azh/edit-mode-map "\C-c\C-c" 'azh/edit-exit))

(defun azh/edit-exit ()
  (interactive)
  (exit-recursive-edit))

そして .zshrc に以下の設定を加える。履歴検索の置き換えなのでCtrl+Rに割り当てておいた。

anything-history ()  {
    tmpfile=/tmp/.azh-tmp-file
    emacsclient --eval '(anything-zsh-history-from-zle)' > /dev/null
    zle -U "`cat $tmpfile`"
    rm $tmpfile
}
zle -N anything-history
bindkey "^R" anything-history

使い方

端末上のzshでCtrl+Rを押すとanythingの画面がポップアップされ、絞り込み可能になる。
anything上でふつうにEnterを押すと、anythingの画面が消え、選択したコマンドがzshにペーストされる。Ctrl+eを押すとEmacs上でコマンドラインを編集することができる(Ctrl+C Ctrl+Cで編集終了)。

最新版のanything.elでは候補選択を大文字のアルファベットで行えるので.emacsに以下の設定を入れておくことをおすすめする。

(setq anything-enable-shortcuts 'alphabet)

注意

初回起動はEmacsフレームを作成するので少し待たされるけど、2回目以降はサクサク動く!!
Emacs Lispコード自体はzshに依存してないため、理屈上ほかのシェルでも動作するだろう。ただ、他のシェルのコマンドラインエディタを操作する方法を知らないので情報求む。

参考:zshでanything.el風履歴検索(その4) - deruiの日記