読者です 読者をやめる 読者になる 読者になる

anything/helmのコマンド動作中のキーバインドを簡単に設定する方法

Emacs anything.el helm.el

anything.el/helm.elをお使いの方の中には、
anything/helmのコマンド実行により表示される候補選択バッファでのキーバインド
ご自分の使いやすいように変更している方もいらっしゃると思います。
私もその一人なのですが、その方法について以前から悩ましい思いを抱えていました。

どういうことかと言うと

通常、キーバインドの変更をするには以下の方法が一般的かと思います。

;; 対象のキーマップの定義を変更する
(define-key hoge-map (kbd "C-n") 'hoge-command)

;; 対象のモードになった時にローカルマップを変更する
(add-hook 'hoge-mode-hook '(lambda () (local-set-key (kbd "C-n") 'hoge-command)))

しかし、anything.el/helm.elでは、この方法は有効でない場合があります。
なぜなら、anything.el/helm.elのコマンドには独自にキーマップが定義されている場合があるからです。
anything.elには、基本となるキーマップとしてanything-mapが定義されていますが、
単純に以下のように変更しても、

;; anything動作中のキーマップを変更
(define-key anything-map (kbd "C-j") 'anything-next-line)

コマンドによっては、変更したanything-mapの定義がさらに上書きされてしまいます。
また、anythingのキーマップはミニバッファ上で有効になるためlocal-set-keyも使うべきではないと思います。

苦肉の力技

というわけで、今までは定義されている全てのキーマップに対してdefine-keyの設定を記述していました。

(define-key anything-map (kbd "C-j") 'anything-next-line)
(define-key anything-c-buffer-map (kbd "C-j") 'anything-next-line)
(define-key anything-find-files-map (kbd "C-j") 'anything-next-line)
...

ちなみに、キーマップのシンボルを抽出して動的に設定しようとしましたが、
define-keyの引数はキーマップ名を明示する必要があるようでだめでした。

動的に設定する他の方法

ですが、以下の設定により、どのコマンドでも変更が有効になるようになりました。

(defvar ~anything-modify-keymap-required nil)
(defvar ~anything-modify-keymap-finished nil)

(defadvice anything-approximate-candidate-number (before mod-keymap activate)
  (when (and ~anything-modify-keymap-required
             (not ~anything-modify-keymap-finished))
    
    ;; ここにキーバインドを羅列
    (define-key anything-map (kbd "C-j") 'anything-next-line)
    
    (setq ~anything-modify-keymap-finished t)))

(defadvice anything-read-pattern-maybe (around mod-keymap activate)
  (let ((~anything-modify-keymap-required t)
        (~anything-modify-keymap-finished nil))
    ad-do-it))

設定内容について

anythingなコマンドが実行されると、
anything-read-pattern-maybeによってミニバッファにプロンプトが表示されますが、
その際、anything-mapは局所的に再定義され、そのコマンド向けに再設定されます。
なので、anything-mapが再設定された後に実行されるanything-approximate-candidate-numberに
キーバインドの処理を追加しています。

ただ、anything-approximate-candidate-numberは他のタイミングでも実行されるため、
判別用の変数を設定して、無駄に処理しないようにしています。

この方法も理想的とは言えませんが、力技よりは格段に良いと思います。

helmは...

さもわかったように書いていますが、私はhelm.elを使ったことがないためわかりません。
anythingをhelmに変えるだけで動作するかも知れないしだめかも知れません。
何かのヒントになれば。

2014/03/04追記 helmの場合、anything-approximate-candidate-numberがhelm-get-candidate-numberでした。

その他

※ ここで言及しているanything.elのバージョンは1.3.9です。