yasnippet.elを256倍にパワーアップ!連続展開と条件分岐テンプレートを使おう

動機

yasnippetの展開機能はなかなか強力ですが、「次の」テンプレート展開の準備や条件分岐ができない弱点があります。

連続展開の例は、プログラミングにおけるドメイン特化言語を記述する場合です。 この場合は、パターンが決まっているので、次のテンプレート展開の準備をするテンプレートを記述すればすんなり記述できるのです。 そして、テンプレート展開後に、次のテンプレートを展開していきます。

条件分岐テンプレートの例は、新規ファイル作成時に文章のテンプレートを選んで展開するケースです。 僕のブログの場合、「自作Emacs Lispリリーステンプレート」、「Emacs Lispを使ってみたテンプレート」、「EmacsのTipsテンプレート」などがあります。

記憶力のいい人ならば、テンプレート名を覚えて普通に展開すると思います。 僕の場合はテンプレート名はおろか、テンプレートを作ったこと自体忘れてしまうのです。 ならば、テンプレート展開の準備をしておけばよいことになります。

それらの弱点は わずか10行足らずのコードで克服することができます。

必要なもの

設定

以下の設定を.emacsに加えてください。

;;; [2010/07/13]
(defun yas/expand-link (key)
  "Hyperlink function for yasnippet expansion."
  (delete-region (point-at-bol) (1+ (point-at-eol)))
  (insert key)
  (yas/expand))
;;; [2010/12/02]
(defun yas/expand-link-choice (&rest keys)
  "Hyperlink to select yasnippet template."
  (yas/expand-link (completing-read "Select template: " keys nil t)))
;; (yas/expand-link-choice "defgp" "defcm")

使い方

テンプレートを準備するには、yas/expand-linkやyas/expand-link-choice関数をテンプレートに記述するだけです。

準備されたテンプレートを展開するには、それらの関数の行にてC-e C-x C-eを押すだけです。 なぜなら、yas/expand-linkは現在行を消し、指定されたテンプレートを展開する関数だからです。

使用例

ここだけだとわかりづらいので具体例を出します。

yas/expand-linkで次に展開されるべきテンプレートを準備する

たとえば、以下のコード(Rubyスクリプト)は、Expectationsブロックの中に複数のexpectブロックが存在します。

require 'rubygems'
require 'expectations'

Expectations do
  expect 2 do
    1+1
  end
  expect "foobar" do
    "foo"+"bar"
  end
end

この場合、expectationsというテンプレートに1つのexpectブロックを記述することもできますが、2つ目以降のexpectブロックは手書きする必要があります。 必要な分だけexpectブロックを楽に入力したいのです。

この場合、次のようにexpectationsとexpectations.expectテンプレートを設定します。

#name : expectations
# --
require 'rubygems'
require 'expectations'

Expectations do
# (yas/expand-link "expectations.expect")$0
end
# -*- mode: snippet -*-
# name: expect
# --
  expect $1 do
    $0
  end
  # (yas/expand-link "expectations.expect")

双方に含まれている「# (yas/expand-link "expectations.expect")」という行がミソです。 この行は、expectations.expectテンプレートを展開する関数なので、展開すれたびに次のexpectations.expectテンプレートの準備ができている状態になります。


expectations

↓ Tab

require 'rubygems'
require 'expectations'

Expectations do
  # (yas/expand-link "expectations.expect")
end

↓ 即座に展開され、yas/expand-linkの行の末尾にカーソルがあるので、C-x C-e

require 'rubygems'
require 'expectations'

Expectations do
  expect  do
    
  end
  # (yas/expand-link "expectations.expect")
end

↓ 展開を完了させる

require 'rubygems'
require 'expectations'

Expectations do
  expect 2 do
    1+1
  end
  # (yas/expand-link "expectations.expect")
end

↓ 次の yas/expand-link の行にてC-e C-x C-eを押し、展開を完了させる

require 'rubygems'
require 'expectations'

Expectations do
  expect 2 do
    1+1
  end
  expect "foobar" do
    "foo"+"bar"
  end
  # (yas/expand-link "expectations.expect")
end

展開が終わると、最後にyas/expand-linkの行が残ります。 将来expectブロックを増やすことを考えたら残しておくべきです。

yas/expand-link-choiceで条件分岐をする

僕は、ブログの原稿の初期テンプレートを設定しています。

以下の設定により ~/memo/hatena/*.org を新規作成するとき、 ~/emacs/insert/org2hatena.insert が挿入されます。


(setq auto-insert-directory "~/emacs/insert/")
(auto-insert-mode 1)
(add-to-list 'auto-insert-alist '("memo/hatena/.+org$" . "org2hatena.insert"))

そして、 org2hatena.insert の内容は以下のようになっています。

# (yas/expand-link "org2hatenakeywords")
# (yas/expand-link-choice "hatena.emacs.release" "hatena.emacs.tips" "hatena.emacs.use")

2行目が yas/expand-link-choice 関数による条件分岐テンプレートです。 C-e C-x C-eで評価すると、hatena.emacs.release、hatena.emacs.tips、hatena.emacs.useのうち、どれかをミニバッファで選択してテンプレート展開します。

ここでは autoinsert.el による初期テンプレートの設定ですが、プログラミングの際にも使えます。 要は、次に展開すべきテンプレートの準備をしておくことが肝要です。