anything-c-yasnippetを使わずにyasnippetをanythingインタフェースで選択する

設定を見直し中 - すぎゃーんメモ シリーズ。
yasnippetを使ったスニペット補完を初めて使い始めている。で、補完の呼び出し方には通常のyas/expandコマンドを呼ぶ以外に、auto-complete-configに含まれるac-source-yasnippetを使う方法や、anythingインタフェースで選択するためのanything-c-yasnippetがある。
yasnippet, anything-c-yasnippetのまとめエントリー - IMAKADO::BLOG
個人的にはauto-completeの補完とyasnippetスニペット補完は別々にしたいな、と思ってanything-c-yasnippetを使おうと思ったのだけど、どうやらコレは最新のyasnippetでは動かないらしい。
yasnippet,anything-c-yasnippetをインストールした - $shibayu36->blog;
yasnippet-0.6.1b(c?)を使うとanything-c-yasnippet.elが動かない - 放牧日記
これらの記事のように、パッチを当てる必要がある。


で、このanything-c-yasnippetyasnippetのソースを読んでいて気付いたのだけど、今のyasnippetにはyas/prompt-functionsという変数が定義されている。ログ見る限りでは2009-07-13、v0.6.0あたりから出てきたのかな?

(defcustom yas/prompt-functions '(yas/x-prompt
                                  yas/dropdown-prompt
                                  yas/completing-prompt
                                  yas/ido-prompt
                                  yas/no-prompt)

複数の補完候補があった場合に、ここに登録されている関数が呼ばれ、ユーザに選択させることになるらしい。それらの関数はプロンプト文字列と、選択候補のリストを引数として受け取り、確定させる場合はそのリスト内の要素を返す。nilが返された場合は次の関数に選択を任せることになるようだ。デフォルトでは上記のように定義されていて、例えばyas/x-promptx-popup-menu関数を使ってユーザに選択させることになる。同じkeyで異なる定義のスニペットを複数登録してある場合や、2つ以上のスニペットが登録してあるモードでyas/insert-snippetを呼んだときはコレが呼ばれるはず。

なので、このprompt-functionsを自分で定義してやれば、anythingインタフェースを簡単に作ることができる。

(defun my-yas/prompt (prompt choices &optional display-fn)
  (let* ((names (loop for choice in choices
                      collect (or (and display-fn (funcall display-fn choice))
                                  coice)))
         (selected (anything-other-buffer
                    `(((name . ,(format "%s" prompt))
                       (candidates . names)
                       (action . (("Insert snippet" . (lambda (arg) arg))))))
                    "*anything yas/prompt*")))
    (if selected
        (let ((n (position selected names :test 'equal)))
          (nth n choices))
      (signal 'quit "user quit!"))))
(custom-set-variables '(yas/prompt-functions '(my-yas/prompt)))

丁寧なことにオプショナルな第3引数としてchoicesリストそれぞれの要素(templateのオブジェクト)から登録してあるテンプレート名を引くための関数が渡されてきてくれるので、それを使って候補名のリストを作って、anything-other-bufferに渡してやるだけで良い。actionでは選択した名前をそのまま渡すようにすることで、変数selectedに選択されたテンプレート名を得ることができる。中断した場合はnilが返ってくるので、quitシグナルを送ってやることで補完も中断される。

これだけで、anything-c-yasnippetを導入してパッチを当てたりしなくても、yasnippetの補完をanythingインタフェースで実現することができた。さくさく選べるようになるのでちょっとしたモノもどんどんsnippetに登録していくと効率が上がりそう。
試しにauto-completeとyasnippetの補完を使ってコードを書く例をyoutubeに上げてみた。

スラスラっと書けるカンジがして良いですね。