yasnippet や emacs lisp の小ネタなど

yasnippet は定型コードなどを素早く展開することが出来る汎用テンプレートフレームワークです。

普通の使い方

yasnippetのドキュメントは本家のページに良くまとまっています。elisp書ける人は全部読むとかなりsnippet力が上がると思いますので、ぜひ読んでみたらいいと思います。

あと、以下のページでもみんなテンション高くなってます。(もう3年くらい前の祭りですが。。。)

基本的に、「あ、いまsnippetひらめいた!」と思った瞬間に、 M-x yas/new-snippet で新規スニペット登録バッファを開いて、 C-c で保存・登録。込み入ったものだと何度かテストと書き換えをすると思います。

以前書いたスニペットの修正は M-x yas/visit-snippet-file (今いるバッファのスニペットから選ぶ)もしくは M-x yas/find-snippets (スニペットディレクトリから探す)で出来ます。

ちなみに、yasnippetとzencodingを合わせると展開プレビューや補完が効きまくってHTMLがすらすら書けるようになるようですのでおすすめです。



Emacs with HTML, Zencoding and YASnippet

組み合わせや干渉など

yasnippetが大量にあると覚えられなくなるので、anythingで絞り込むという方法があります。以下のページが参考になると思います。こちらはsnippetを選ぶだけでなく、新規追加などのアクションもあります。

auto-completeのソースにしてしまって、打った端からプルダウンで見せるという方法もあります。auto-completeに標準で含まれていますので、 ac-source に ac-source-yasnippet が入っていれば使えます。 auto-complete-config.el を確認してみてください。

yasnippetと干渉するものがいくつかあります。

flymakeとの干渉については antipopさんの記事 yasnippet関連の設定 - Kentaro Kuribayashi's blog に設定例があります。

auto-complete を展開中に無効にする設定を以下のgistに書いてみました。

基本的に yas/before-expand-snippet-hook と yas/after-exit-snippet-hook のhookで他の機能を一時停止したり再開させると良いと思います。

自分snippets

デフォルトではいくつかテンプレートがありますが、なかなか人の作った短縮コードは覚えられないものです。なので最初から用意されているテンプレートは参考程度にしておいて、自分で登録していって増やしていくのが良いのではないかと思います。

自分はあんまり数は多くないです。例えばelispは以下を登録しています。

展開元 展開先
defvar da (defvar
internal コメントで int [internal]
let let (let ((
defun df (defun
interactive 非コメントで int (interactive)
lambda la (lambda () )
lexical-let ll (lexical-let ((

let が「(let ((」になるだけでもかなり違います。ちなみに、これはimakado君に教えてもらいました。

上の例では文字が入るだけではなくて、いろいろ仕掛けが入っています。例えば、defvar, defun は、展開時に前後のコードを見に行って、それらしいプレフィクスを探してきて自動で入れます。さらに、引数の文字列からdocstringを強制的に生成するお節介機能もあります。



defun展開の様子

defun の yasnippet

# name : defun template
# group : defun 
# key : df
# contributor : kiwanami
# --
(defun ${1:`(save-excursion
  (let ((re "(def\\\\(un\\\\|var\\\\)[ \t\n]*\\\\([a-z0-9]*[:-]\\\\)"))
    (cond
     ((or 
       (re-search-backward re nil t)
       (re-search-forward  re nil t))
      (match-string 2))
     (t ""))))`}${2:func-name} (${3:args})
  "$2
${3:$(mapconcat 'upcase (split-string (replace-regexp-in-string "&[a-z]+" "" yas/text)) " \n")}"
  $0
  )

このように、かなり本気の elisp が書けます。参考になればと思います。

注意点として、文字列の中のバックスラッシュが一度展開されてしまうようなので、正規表現を書く場合はバックスラッシュを多めに書く必要があります。

デバッグが難しいので、小さなものから書いていったり、やっぱり yasnippet のコードを呼んだりしてブレークポイントを仕掛けるのが早いです。

はまり所としては、初期値が無いフィールドが隣接していると、フィールドが消えてしまいます。「$0」とかは要注意です。これはフィールドの位置をマーカーオブジェクトで印をつけていて、そのため開始終了位置が重なってしまうとマーカーがくっついて離れなくなってしまうからです。実装上の制限だと思いますので、フィールドが消えてしまう現象で困った場合は、スペースなどで間をあけると良いと思います。

いくつかはここのgistにおいています。

その他テク

やっぱり、yasnippetの応用技の解説はそれほど多くないのですがいくつか紹介してみます。

ありえる社の深町さんが、昨年の Software Designyasnippet の記事を書かれています。(ごめんなさい。まだ読んでないです!><)


るびきちさんは、連続して展開しやすくする工夫を紹介されています。

yasnippet.elを256倍にパワーアップ!連続展開と条件分岐テンプレートを使おう - (rubikitch loves (Emacs Ruby CUI Books))


やっぱり、yasnippetのすごさは目の前で使っているのを見るのが一番いいと思います。ということで、近所のEmacs勉強会にぜひ参加してみましょう。

おまけ: let の * を切り替える

lisp系で let とかの中身を書いているときに、「*」を付けたり消したりするのを思い出して行ったり来たりすることが多かったので、一発でトグルできる機能を書いてみました。

let, lexical-let の変数宣言リストの中(下のコードのhereのあたり)で、 toggle-let-astah を実行するとletの「*」を切り替えます。

(let
  (      ; here
   (a 1) ; here
   (b 2) ; here
   )
   body...)

自分の所では C-8 に割り当てています。(US配列で*があるのと、C-[数字]を使わないので)

S式を見ながらletを探しに行きますので、予想もしないところのletを書き換えたりはしないはずだと思います。正規表現を書き換えれば、他のものにも使えると思います。

以上。小ネタでした。