Hatena::ブログ(Diary)

gan2 の Ruby 勉強日記 このページをアンテナに追加 RSSフィード

この日記のはてなブックマーク数 PV/ 870423 Subscribe with livedoor Reader 役立つリンク集

2008-04-02

yasnippet を導入してみた

yasnippet がなんだかすごい盛り上がっているようなので使ってみることにした。

導入手順

yasnippet関連の設定 - antipopや、anything-c-yasnippetをcodereposにコミットしました - IMAKADOの日記で紹介されていたものを自分なりにまとめてみたら、.emacs に追加する内容はこんな感じになった。

;;; yasnippet の設定
;; 参考 http://d.hatena.ne.jp/antipop/20080321/1206090430
(require 'yasnippet)
;; メニューは使わない
;; (setq yas/use-menu nil)
;; トリガはSPC, 次の候補への移動はTAB
(setq yas/trigger-key (kbd "SPC"))
(setq yas/next-field-key (kbd "TAB"))
;; ポップアップを dropdown-list にする (効果なし?)
(require 'dropdown-list)
(setq yas/text-popup-function #'yas/dropdown-list-popup-for-template)
;; コメントやリテラルではスニペットを展開しない
(setq yas/buffer-local-condition
      '(or (not (or (string= "font-lock-comment-face"
                             (get-char-property (point) 'face))
                    (string= "font-lock-string-face"
                             (get-char-property (point) 'face))))
           '(require-snippet-condition . force-in-comment)))
;; 複数のディレクトリからスニペットを読み込む。
;; yasnippet の snippet を置いてあるディレクトリ
(setq yas/root-directory (expand-file-name "~/.emacs.d/snippets"))
;; 自分用スニペットディレクトリ(リストで複数指定可)
(defvar my-snippet-directories
  (list (expand-file-name "~/.emacs.d/my-snippets")))
;; yasnippet 公式提供のものと、
;; 自分用カスタマイズスニペットをロード同名のスニペットが複数ある場合、
;; あとから読みこんだ自分用のものが優先される。
;; また、スニペットを変更、追加した場合、
;; このコマンドを実行することで、変更・追加が反映される。
(defun yas/load-all-directories ()
  (interactive)
  (yas/reload-all)
  (mapc 'yas/load-directory-1 my-snippet-directories))
;;; yasnippet展開中はflymakeを無効にする
(defvar flymake-is-active-flag nil)
(defadvice yas/expand-snippet
  (before inhibit-flymake-syntax-checking-while-expanding-snippet activate)
  (setq flymake-is-active-flag
        (or flymake-is-active-flag
            (assoc-default 'flymake-mode (buffer-local-variables))))
  (when flymake-is-active-flag
    (flymake-mode-off)))
(add-hook 'yas/after-exit-snippet-hook
          '(lambda ()
             (when flymake-is-active-flag
               (flymake-mode-on)
               (setq flymake-is-active-flag nil))))
;; yasnippet の anything インターフェース anything-c-yasnippet
;; http://d.hatena.ne.jp/IMAKADO/20080324/1206370301
(require 'anything-c-yasnippet)
(setq anything-c-yas-space-match-any-greedy t)
(global-set-key (kbd "C-c y") 'anything-c-yas-complete)
;; anything-c-yasnippet を使うモードの登録
(add-to-list 'yas/extra-mode-hooks 'ruby-mode-hook)
(add-to-list 'yas/extra-mode-hooks 'cperl-mode-hook)
;; yasnippet の初期化
(yas/initialize)
(yas/load-all-directories)

スニペットの置き場所

id:antipop さんは自分用のスニペットを保存するディレクトリをいくつかに分けていたけど

僕は今のところ1つで十分なので、公式の snippets ディレクトリと同じ場所に、

my-snippets ディレクトリを作ってそれを自分用のディレクトリにすることにした。

my-snippets は snippets をコピーして名前を書き換えてしまうのがいいと思う。

で、僕は ruby-mode のスニペットの1行ブロックが、外人さんの好きな

each { |e| code }

って書き方になってるのが気に入らなかったので

each {|e| code }

に直しておいた。

こういう作業は moccur-grep-find 使うと早い。

~/.emacs.d/my-snippets/ruby-mode/ を "{ |\$e|" で検索して結果を書き換えた。

(moccur-grep-find の詳しい使い方についてはこちらを参照のこと → Emacs で wdired と moccur-edit を使っていない人は(ry - ひげぽん OSとか作っちゃうかMona-)


dropdown-list.el について

dropdown-list.el は正直よく分からない。

もしかしたら機能しているのかもしれないけど

僕の Meadow3 では設定をしても何も変化が見られない。

EmacsWiki: dropdown-list.el


anything-c-yasnippet.el について

仕事早い!すばらしい!

でも1つだけ気になることがある。

選択後にminibufferにどのkeyに割り当てられているが表示されるのでよく使うsnippetは覚えられるというメリットもあります。

yasnippet, anything-c-yasnippetのまとめエントリー - IMAKADO::BLOG

この機能なんだけど、一覧で表示できるようにしてもらえたらすごく嬉しい!

例えば、ruby-mode で anything-c-yas-complete を呼び出したときに以下のように列挙されるけど

all? { |...| ... }
any? { |...| ... }
attr_accessor ...
attr_reader ...
attr_writer ...

これをこんな感じにしてもらえたらいいなぁ…。

all     | all? { |...| ... }
any     | any? { |...| ... }
rw      | attr_accessor ...
r       | attr_reader ...
w       | attr_writer ...

理由は2つある。

1つ目。僕は auto-save-buffers を使って

キーボード入力後 0.5 秒間何もしないと自動的にファイルが保存されるようにしている。

だから、補完後にミニバッファに表示されたメッセージは

すぐに上書きされてしまうので、確認している余裕があまりない。

2つ目。キーの割り当てを確認するときは一覧で見れた方が便利だと思う。


もしかしたら、anything のインターフェースを使うために

わざと上のようなことをしていないのかもしれないけど

そうだとしたらごめんなさい!


TAG がトリガーだと挙動があやしい

でも1ついやんなところがあって、1つのsnippetを発動させるとそのsnippetのすべての入力欄をTABキーで移動して書き終わらないと次の snippetが発動しないような挙動になってるようです。また、そのsnippetをすべて書き終わる前にその部分を消去してしまうと、それ以降の snippetが発動しないっぽい。でもなにかの弾みで発動したり…。どんな条件でリセットされてるのかよくわかりませんでした。

yasnippet.elをインストールしてみた - Clouder::Blogger

Clouder さんの言っているいやんな現象は僕の環境でも再現された。

Clouder さんの yasnippet は 0.3.1 の bundle で

僕が今回インストールした yasnippet は 0.4.4 の bundle じゃないやつだけど

現在最新の 0.4.4 でもこの問題はまだ改善されてないみたい。

でもトリガーを TAB から SPC にしたらおかしな挙動はなくなった。

C-g で必ずリセットもかけられる。これで安心。


SPC トリガー万歳!

トリガーを SPC にするっていうのは id:antipop さんのアイディアなわけだけど、これお勧めです。

SPC の方が TAB より絶対気持ちよく使える。

例えば、ruby-mode で ruby-electric を使っている人は SPC トリガーを併用することで

あたかも ruby-electric がパワーアップしたかのように使える。

any まで入力して SPC 押すと any?{|e| } って展開されるとかね。

ruby-electric と同じインターフェースruby-electric だけじゃできなかったことができるようになる。


僕は RSpec の describe とか before とか it とかを

ruby-electric が補完してくれたらなぁ…と思っていて、

実は yasnippet を試す前は、このために ruby-electric を拡張しようかと考えていた。

でも yasnippet + SPC トリガーのおかげでその必要はなくなった。

ruby-electric を無理にいじらなくても

yasnippet に新しいスニペットを追加すればいいことが分かったからだ。


一例を挙げると、describe 用のスニペットはこんな感じだ。

#name : describe ..., "..." do ... end
# --
describe ${1:klass}, "${2:when}" do
  $0
end

このコードは ~/.emacs.d/my-snippets/text-mode/ruby-mode/desc として保存している。

細かい仕様はまだよく分からないけど、スニペットを作るのはあまり難しくはない。

ダウンロードしたときにいっしょについてくる他のスニペットを読めば

どう書けばいいかは何となく分かると思う。


その他

  • 新しいスニペットを追加したら、「M-x yas/load-all-directories」を使うことでロードできる。
  • if SPC した後に、そこから C-b SPC 繰り返すとおもしろい。
  • TAG トリガーの挙動があやしい原因は思いつく限り3つ。
    • Meadow
    • auto-save-buffers との相性問題
    • インデント

なんか久しぶりに長いエントリを書いた。


参考

IMAKADOIMAKADO 2008/04/02 21:50 >一覧で表示できるようにしてもらえたらすごく嬉しい!
先ほどカスタマイズ可能な変数 anything-c-yas-display-key-on-candidate を追加した0.4をコミットしました。
現在のところデフォルトnilですがtにすると補完候補の表示にkeyを含めるようになります。

ナイスアイデアどうもです。
確かにパターンは前方一致では無いので表示しちゃったほうが便利ですね!
様子を見てデフォルトで表示にするか決めようと思います。それでは。

gan2gan2 2008/04/03 20:16 やっぱり仕事早いですね!
どうもありがとうございます!
試してみました。
コメントとパッチを貼っておきます。

主観的なコメントです。
key は左端に揃えて表示した方が見やすいと思います。
また [ ] で囲まない方が見やすいと思います。

上記の主観に合うように自分好みのフォーマットに書き直してみました。
以下はオリジナルと書き直しバージョンの差分です。
key が 12 文字以下であることを期待しています。

Index: anything-c-yasnippet.el
===================================================================
--- anything-c-yasnippet.el(revision 8785)
+++ anything-c-yasnippet.el(working copy)
@@ -222,7 +222,7 @@
for name = (car dotlst)
for template = (cdr dotlst)
for key = (anything-c-yas-get-key-by-template template alist)
- for name-inc-key = (concat ”[” key ”] ” name)
+ for name-inc-key = (format ”%-12s | %s” key name)
collect `(,name-inc-key . ,template))))
;; default ex: for (...) { ... }
(t

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証