※原文: http://yasnippet.googlecode.com/svn/trunk/doc/snippet-expansion.html
重要: このドキュメントは YASnippet の SVN trunk に対して適用されるものです。 SVN trunk はこちらから取得できます。 他のバージョンに対するドキュメントはこちらで見られます。
YASnippet でスニペットを展開するのにさまざまなやり方があります:
省略形(スニペットの trigger key)をタイプし、yas/trigger-key
に定義されているキーを押す(デフォルトでは "TAB" キー)。
この手順はマイナーモード yas/minor-mode
が有効なバッファで利用できます。
コマンド yas/insert-snippet
を実行する(M-x yas/insert-snippet
とタイプするか、キーバインドを使う)。
この手順は yas/minor-mode
が有効であることを必要としません。
アクティブなスニペットに関連付けられているキーバインドを使う。
この手順もまた yas/minor-mode
がアクティブである必要があります。
メニューバーにある "YASnippet" メニューから直接展開する。
hippie-expand を使う。
emacs-lisp コードから展開する。
yas/minor-mode
が有効になると、yas/trigger-key
で与えられたキーバインドが有効になります。
yas/trigger-key
は yas/expand
を呼び出します。これは、スニペット省略形(snippet key とも呼ばれます)をカーソル位置の前に展開しようとします。
デフォルトのキーは "TAB
" ですが、自由にほかのキーに設定することができます。
すべてのバッファで YASnippet マイナーモードを有効にするには、yas/global-mode
コマンドを使います。
yas/global-mode
を使っているが、あるバッファでは YASnippet を無効化したい、というときには、そのバッファのモードのフックでバッファローカル変数 yas/dont-active
を設定してください。
yas/trigger-key
の利用とその理解にまつわるトラブルは、YASnippet でもっとも話題になりやすいものです。
FAQ を参照してください。
yas/fallback-behaviour
は設定可能な変数で、デフォルトでは 'call-other-command
に束縛されています。
展開すべきスニペットを yas/expand
が見つけられなかった場合、マイナーモードを一時的に無効にして、yas/trigger-key
に束縛されている他のコマンドがないか探します。
もし他のコマンドが見つかった場合、その見つかったコマンドが呼ばれます。 たいていの場合、この仕組みはとてもうまく機能します。 スニペットがあったらそれを展開し、そうでなければ trigger key にもともと割りつけられていたコマンドを呼ぶ、というわけです。
また一方で、変数 yas/fallback-behavior
をカスタマイズすることで、この挙動を変えることができます。
この変数の値を 'return-nil
に設定すると、スニペットが見つからなかったときにもともとのコマンドを呼ぶ代わりに、nil
を返すようになります。
コマンド M-x yas/insert-snippet
は、現在のメジャーモードにしたがって、スニペットをカーソル位置に挿入できるようにしてくれます。
このコマンドを実行すると、最初に snippet key (訳注:スニペット省略形)の入力をうながします。
同じ snippet key に対して複数のテンプレートがある場合、続けてスニペットテンプレートの入力をうながします。
条件判断システムにしたがって、カーソル位置に挿入可能なスニペットからなるリストが表示されます。
現在のメジャーモードに対して適用可能なすべてのスニペットを見たいときには、コマンドの前に C-u
を付けてください。
入力をうながすメソッドもまた、yas/prompt-functions
によって制御されています。
"Writing Snippets" の章の # binding:
ディレクティブの記述を参照してください。
"the YASnippet Menu" の章を参照してください。
hippie-expand
での展開時には、Emacs-Lisp コードから直接スニペットを展開したいことがあるかもしれません。
そんなときは yas/expand
の代わりに yas/expand-snippet
を呼ぶようにしてください。
メニューバーから展開するのと同様に、条件判断システムおよび複数候補は展開に影響しなくなります。 実際、the YASnippet menu からの展開は、以下のコードの評価とまったく同じ効果を持っています:
(yas/expand-snippet template)
もっと情報が必要な場合は yas/expand-snippet
の内部ドキュメントを参照してください。
現在のカーソル位置に展開できるスニペットはどれか、を見つけ出すために、YASnippet はきわめてわずかなフィルタリングしかしません。
具体的に言うと、以下のものが重要です:
現在読み込み済みの snippet table
あなたのファイルシステムのディレクトリ階層構造から読みだされる情報です。
"Organizing Snippets" の章を参照してください。
これらのテーブルは html-mode
や ruby-mode
のようにメジャーモードの名前を付けられています。
現在のバッファのメジャーモード
現在のメジャーモードが読み込み済みの snipet table のひとつにマッチした場合、
そのテーブルにあるスニペットはすべて展開の対象となります。
現在のメジャーモードを確認するためには、M-x describe-variable RET major-mode RET
を使ってください。
Parent table
展開可能な他のテーブルの親として定義されている snippet table もまた、展開可能とみなされます。 この解釈は再帰的に行われます。つまり、展開可能なテーブルの親の親もまた展開可能とみなされます。
バッファローカルな変数 yas/mode-symbol
この変数は、メジャーモードに一致しない名前の snippet table を展開可能とみなすために使うことができます。
この変数にある名前を設定すると(たとえば rinari-minor-mode
とか)、そのマイナーモードでしか展開されないスニペットを使うことができます。
もちろん、この設定を条件によって切り替えたいと思うこともあるでしょう(たとえば、あるマイナーモードに入った時にだけ、など)。
そんなときはフックを使うのがよいアイディアです。
;; When entering rinari-minor-mode, consider also the snippets in the
;; snippet table "rails-mode"
(add-hook 'rinari-minor-mode-hook
#'(lambda ()
(setq yas/mode-symbol 'rails-mode)))
バッファローカルな変数 yas/buffer-local-condition
この変数を使うと、現在のバッファ内でどのスニペットが展開可能かをより細かい粒度で制御することができます。 たとえば、デフォルトの値では、コメントおよび文字列リテラルの中ではスニペットを展開しないようになっています。 詳細については "The condition system" の章を参照してください。
次のシナリオを考えてみてください:
あなたはベテランの Emacs ハッカーです。
あなたは省略表記を好んでいて、yas/trigger-key
に "SPC
" を設定しています。
また一方では、(たとえば python-mode
などで)コメントブロックや文字列の中でタイプした "if
" はスニペットとして展開してほしくありません。
# condition :
ディレクティブ("Writing Snippets" の章を参照)を使っている場合、
if
に対して (not (python-in-string/comment))
という条件を指定するだけで済みます。
しかし、while
や for
などについてはどうでしょう?
すべてのスニペットに対して同じ条件を書くのは本当にうんざりするでしょう。
そんなわけで、バッファローカル変数 yas/buffer-local-condition
があります。
python-mode-hook
の中で、この変数に (not (python-in-string/comment))
を設定すればよいのです。
次に、ある特定のスニペットだけはコメントの中でも展開できるようにしたいと思ったら、どうしたらよいでしょう? これも実現可能です! でも、話を進めるのを止めて、以下のルールについて見てください:
もし、yas/buffer-local-condition
を評価すると nil になるなら、どのスニペットも展開可能とはみなされません。
もし、評価した結果がコンスセルで、そのコンスセルの car
部がシンボル require-snippet-condition
で、
そのコンスセルの cdr
部がひとつのシンボル(これを requirement
と呼ぶことにしましょう)である場合、
# condition:
ディレクティブを持たないスニペットは、適用可能とはみなされません。
# condition:
ディレクティブの評価結果が nil だった(または評価中にエラーが起きた)スニペットは、展開可能とはみなされません。
スニペットが # condition:
ディレクティブを持っていて、それを評価した値が nil 以外だった場合(その値を result
と呼ぶことにしましょう):
もし requirement
が t
なら、そのスニペットは展開可能である。
もし requirement
が result
と eq
なら、そのスニペットは展開可能である。
それ以外の場合、そのスニペットは展開可能とはみなされません。
もし、評価した結果がシンボル always
だった場合、# condition:
ディレクティブに関係なく、すべてのスニペットは展開可能とみなされます。
もし、評価した結果が t
または他の non-nil な値だった場合:
もしスニペットが # condition:
ディレクティブを持っていないか、# condition:
ディレクティブを評価した値が non-nil だった場合、
そのスニペットは展開可能である。
それ以外の場合、そのスニペットは展開可能とはみなされません。
ここで述べられているシナリオでは、yas/buffer-local-condition
を以下のように設定してください:
(add-hook 'python-mode-hook
'(lambda ()
(setq yas/buffer-local-condition
'(if (python-in-string/comment)
'(require-snippet-condition . force-in-comment)
t))))
・・・・また、コメントの中でも展開されてほしいスニペットの # condition:
ディレクティブは、シンボル force-in-comment
と評価されるようにしてください。
こうすると、if
のような他のスニペットはコメントの中では展開されないままで、あなたが展開したいスニペットは展開できるようになります。
上のほうで概要を述べたルールは、現在のカーソル位置に展開可能なスニペットを複数返すことがありえます。
複数の候補が見つかったとき、YASnippet はあなたにひとつ選ばせるでしょう。
複数の候補を選択するためのユーザインタフェースは、yas/prompt-functions
を通じてカスタマイズすることができます。
これは、スニペットに対する指示を入力するときにあなたが使いたいメソッドを定義するためのものです。
これをカスタマイズするには、M-x customize-variable RET yas/prompt-functions RET
としてください。
他のやり方として、あなたの emacs 設定ファイルに以下のコードを書いてください:
(setq yas/prompt-functions '(yas/x-prompt yas/dropdown-prompt))
現在、YASnippet にはほかの代替手段もあります。
関数 yas/x-prompt
は、なにかを選ぶためにポップアップメニューを表示するのに使えます。
このメニューはあなたの環境のネイティブなウインドウシステムの部品 (widget) です。
つまり:
見た目がきれいです。たとえば、gtk をサポートするように Emacs をコンパイルしたなら、このメニューは gtk のテーマにしたがって描画されるでしょう。
このメニューでは Emacs による制御はほとんどできません。たとえば、C-n
や C-p
では操作できません。
この関数はターミナル上では使えません。
昔ながらの emacs の補完メソッドのためには関数 yas/completing-prompt
が使えます。
これよりずっと見た目のよいメソッドとしては yas/ido-prompt
が使えます。
実際に試してみるのが一番いいです。
この機能はターミナル上でも使えます。
dropdown-menu.el
を使う関数 yas/dropdown-prompt
も yas/prompt-functions
リストに含めることができます。
これはウインドウシステム上でもターミナル上でも機能し、カスタマイズ可能で、C-n
や C-p
で操作でき、
q
で quit でき、6番目の候補を選択するのに 6
を押すこともできます。
変数 yas/prompt-functions
のドキュメントを参照してください。
(日本語訳: Seaoak, 2010/Dec/15)