anything-migemoをhelmに対応させてみた

Emacs使いの方々、helmはご存知でしょうか。

詳しくはリンク先を確認して頂ければわかりますが、あのanythingからforkした実装です。github上のページでも、以下のように記述されています。

Helm is incremental completion and selection narrowing framework for Emacs. It will help steer you in the right direction when you're looking for stuff in Emacs (like buffers, files, etc).

Helm is a fork of anything.el originaly written by Tamas Patrovic and can be considered to be its successor. Helm sets out to clean up the legacy code in anything.el and provide a cleaner, leaner and more modular tool, that's not tied in the trap of backward compatibility.

つまり、anything.elに互換性維持のために残されていたlegacyなコードを削除して、より洗練された形にしよう、ということらしいです。そのためには互換性にあまり縛られることはない、という方針のようです。

とは言っても、現状ではほとんどのanythingで利用していたコードが、anything-*をhelm-*に変更するだけで動作していたりします。
開発の流れも非常に早いため、この先どうなるかはよくわかりませんが。

ただ、今のhelmは、見たところでは元々anything-config.elにあった設定を外出しして、実装を整理しているみたいです。その過程でprefix-shortcutとかが無くなったり、各種sourceに色々機能が追加されたりしています。ま、普通に使う分にはあまり気になりませんでしたが。

helmはまだまだ主流になっていない為か、現在大変お世話になっている各種elispのhelm版というのは出ていません。
その中でも、日本人が利用する上で重要なanything-migemoが無いことが気になったので、試しにanything-migemo.elをhelmに対応させてみました。

・・・と書きましたが、実際にはanything-migemo.elのanything-*を全てhelm-*に置き換えたら至極あっさりと動作してしまいました。恐るべし。ちなみにここでは書きませんでしたが、anything-project.elについても、anythingをhelmに書き換えたら見ため上問題無いくらいに動作したりしています。

が、これだけじゃオチが無いので、anything-migemoをhelm版に変更したものを以下に貼っておきます。コピペしてhelm-migemo.elとすれば動作するかと思います。

(eval-when-compile (require 'helm))
(require 'migemo nil t)
(require 'helm-match-plugin nil t)

(defvar helm-use-migemo nil
  "[Internal] If non-nil, `helm' is migemo-ized.")
(defun helm-migemo (with-migemo &rest helm-args)
  "`helm' with migemo extension.
With prefix arugument, `helm-pattern' is migemo-ized, otherwise normal `helm'."
  (interactive "P")
  (let ((helm-use-migemo with-migemo))
    (apply 'helm helm-args)))

(defvar helm-previous-migemo-info '("" . "")
  "[Internal] Previous migemo query for helm-migemo.")
(defun* helm-string-match-with-migemo (str &optional (pattern helm-pattern))
  "Migemo version of `string-match'."
  (unless (string= pattern (car helm-previous-migemo-info))
    (setq helm-previous-migemo-info (cons pattern (migemo-get-pattern pattern))))
  (string-match (cdr helm-previous-migemo-info) str))

(when (memq 'helm-compile-source--match-plugin helm-compile-source-functions)
  (defun* helm-mp-3migemo-match (str &optional (pattern helm-pattern))
    (loop for (pred . re) in (helm-mp-3-get-patterns pattern)
          always (funcall pred (helm-string-match-with-migemo str re))))
  (defun helm-mp-3migemo-search (pattern &rest ignore)
    (helm-mp-3-search-base migemo-forward migemo-forward bol eol))
  (defun helm-mp-3migemo-search-backward (pattern &rest ignore)
    (helm-mp-3-search-base migemo-backward migemo-backward eol bol)))
;; (helm-string-match-with-migemo "日本語入力" "nihongo")
;; (helm-string-match-with-migemo "日本語入力" "nyuuryoku")
;; (helm-mp-3migemo-match "日本語入力" "nihongo nyuuryoku")
(defun helm-compile-source--migemo (source)
  (if (not (featurep 'migemo))
      source
    (let* ((match-identity-p
            (or (assoc 'candidates-in-buffer source)
                (equal '(identity) (assoc-default 'match source))))
           (use-match-plugin
            (memq 'helm-compile-source--match-plugin helm-compile-source-functions))
           (matcher (if use-match-plugin
                        'helm-mp-3migemo-match
                      'helm-string-match-with-migemo))
           (searcher (if (assoc 'search-from-end source)
                         (if use-match-plugin
                             'helm-mp-3migemo-search-backward
                           'migemo-backward)
                       (if use-match-plugin
                           'helm-mp-3migemo-search
                         'migemo-forward))))
      (cond (helm-use-migemo
             `((delayed)
               (search ,@(assoc-default 'search source) ,searcher)
               ,(if match-identity-p
                    '(match identity)
                  `(match ,matcher
                          ,@(assoc-default 'match source)))
               ,@source))
            ((assoc 'migemo source)
             `((search ,searcher)
               ,(if match-identity-p
                    '(match identity)
                  `(match ,matcher))
               ,@source))
            (t source)))))
(add-to-list 'helm-compile-source-functions 'helm-compile-source--migemo t)

(defmacro helm-migemize-command (command)
  "Use migemo in COMMAND when selectiong candidate by `helm'.
Bind `helm-use-migemo' = t in COMMAND."
  `(defadvice ,command (around helm-use-migemo activate)
     (let ((helm-use-migemo t)) ad-do-it)))

ちなみに、anything-migemo.elに付属していたテストコードは動作しなかったため、上記からは除外しています。
anything-test-candidatesに対応するのが無いようで。

使いかたは元々の anything-migemoと変わりません。それでは良いhelmライフを。

追記

helm-find-files の使い勝手は結構微妙です・・・。