スニペットプラグインについて 2020 年版

2020/09/28に公開

はじめに

2020秋 Vim のファイラー系プラグイン比較 に代表されるように、最近プラグインの比較というのがブームになっているようだ。

自分は最近、スニペットプラグインを開発中なのでその過程で既存のスニペットプラグ
インの調査を行っていた。その成果を皆にも紹介しようかと思う。

スニペットプラグインとは

プラグインを紹介する前に、スニペットプラグインとは何なのかというのが分からない
人のために簡単に説明する。スニペットプラグインはよく使う文章を定型文(スニペッ
ト)として登録し、自由に挿入できる入力補助のためのプラグインのことである。
スニペットプラグインを呼び出すためには、ショートカット(スニペットトリガー)を覚
えなくてはいけない。スニペットトリガーを覚えるのは手間なので、自動補完プラグイ
ンを組み合わせてスニペットトリガーを補完してしまうのが個人的に推奨される。

スニペットプラグインに良く似たプラグインとしてテンプレート展開プラグインや、
emmetというのも存在する。
テンプレート展開は主に新規ファイルを作成したときに自動で雛形を作成してくれるも
のである。
emmet は html タグを簡潔に記述されるためのもので、 Web 開発で良く使用される。
スニペットプラグインでも似たようなことができるものの、スニペットプラグインのほ
うがより汎用性が高いので、専用プラグインには使い勝手が劣る。

スニペットプラグインを使う利点としては、インサートモードだけで完結できること、
他からコピペせずに定型的な構文を挿入できるところにあると思われる。
例えば、if 文を入力するには細かい構文をいちいち覚えていなければならないし入力量
が多い。他からコピーアンドペーストするにしてもコピーアンドペーストするために
ノーマルモードに戻らなければならずインデントの調整も必須だ。
スニペットプラグインの場合は定型文を一発で入力できてノーマルモードに戻る必要も
なく、インデントも自動で調整してくれてユーザーの入力が必要な部分のみ入力すれば
よくなる。うまく使えばコードの作成を効率的に行えるのだ。

スニペット用語について

ここではスニペットプラグインを使う上でよく出てくる基本的な機能や用語について簡
単に解説する。

スニペットトリガー

スニペットトリガーとは、スニペットを展開するためのショートカットキーにあたるも
のである。

snippet     if
    if (${1:#:condition}) {
        ${0:TARGET}
    }

上記の例では if がスニペットトリガー、if (${1:#:condition}) {... がスニペッ
トを展開したときに展開されるテキストという意味である。

タブストップ

タブストップとはスニペット展開後にジャンプする位置のことである。
タブストップからタブストップへは一発でジャンプすることが可能である。
一般的にタブストップからタブストップへは <Tab> キーでジャンプすることが多いの
<Tab> をストップさせる位置としてタブストップと呼ばれるのだと考えられる。

snippet     if
    if ${1} {
        ${2}
    }

上記の例ではスニペット展開後に ${1} に飛び、その後 ${2} にジャンプすること
ができる。

プレースホルダー

プレースホルダーはタブストップの一種であるが、デフォルトテキスト等を持ってい
る。デフォルトテキストはタブストップで決まったパターンを入力することが多いとき
に役立つ。

snippet     if
    if ${1:foo} {
        ${2:bar}
    }

上記の例ではスニペット展開後に ${1:foo} に飛び、foo がデフォルトテキストとし
て選択される。

ミラー

ミラーはユーザーの特定の入力をコピーするものである。

for (${1:int} ${2:i} = ${3:0}; $2 < ${4}; $2++) {
    ${0}
}

上記の例では、${2:i} の入力が複数箇所にコピーされる。手でコピーアンドペースト
するより楽である。

ミラーの応用として、ユーザーの入力を変換して展開する機能(トランスフォーム)をサ
ポートしているスニペットプラグインも存在する。

スニペットプラグイン詳細

ここからは代表的なスニペットプラグインについて解説していく。スニペットプラグイ
ンにはそれぞれ利点と欠点があり、完璧なものは存在しない。 この記事があなたのスニ
ペットプラグイン選択の一助となれば幸いだ。

SnipMate

もはや古典となりつつあるスニペットプラグインである。基本的な機能は揃っており、
不自由を感じることは少ないのではないだろうか。欠点としては開発がそれほど活発で
はないこと、他のスニペットプラグインと比較すると機能的に物足りないと感じるかも
しれない。

利点

  • 安定している

  • 必要十分な機能

欠点

  • 開発は停滞

neosnippet.vim

私が開発しているスニペットプラグインであり、neo シリーズの一つ。
実はかなり歴史が古いプラグインで、SnipMate の提供する機能を参考に開発された。

SnipMate とスニペットファイルに互換性があり、SnipMate のスニペットを読み込むこ
とが可能になっている。

開発の裏話としては、thinca 氏が SnipMate をベタ褒めしていたのだが自分にはスニ
ペットプラグインの利点が分からなかった。スニペットプラグインはスニペットトリ
ガーをいちいち記憶するのが面倒だと感じていた。当時開発中であった自動補完プラグ
インの neocomplcache.vim と連携したスニペットプラグインを開発すればスニペットの
良さが分かるのではないかと思ったというのが開発の始まりである。

タブストップの位置を検出するためにプラグインの実装を複雑にしたくなかったので、
マーカーを用いてタブストップの位置を検出している。そのためマーカーテキストが
バッファに挿入される。

欠点としては、マーカー式であるが故に後ろ方向へのジャンプへ対応できないこと、
conceal を設定しないとマーカーが見えてしまうこと、マーカーテキストによりバッ
ファーの中身が変わってしまうことが挙げられる。

現在 neosnippet の開発は終了しており、後継となる deoppet.nvim を開発中だ。

利点

  • SnipMate のスニペットファイルを読み込むことが可能

  • 依存関係がない

  • 安定している

  • 補完プラグインとの連携

欠点

  • マーカー, conceal を使用しているが故の副作用

  • 巨大なスニペットを読み込むと遅い

  • 開発終了

vim-vsnip

VSCode のスニペットとの互換性を重視して設計されたスニペットプラグインである。
Vim script のみで実装されているが故に依存性はない。
シンプルで副作用の少ないスニペットプラグインがよいなら向いている。
LSP のスニペットは VSCode のスニペット形式をベースに作られており、LSP プラグイ
ンや補完プラグインとの連携が実装されているので、LSP との互換性が高い。
スニペットファイルが JSON 形式なので、VSCode のスニペットをそのまま使用するのは
よいが、スニペットを自作するのが大変である。

利点

  • VSCode のスニペットとの互換性

  • LSP との高い互換性

  • 依存性がない

  • 開発が活発

欠点

  • サポートしているスニペット形式が JSON のみ、編集しづらい

  • 他のプラグインと比較するとサポートしている機能は少ない

UltiSnips

名実共に最強のスニペットプラグインであり、この機能に完全に対抗できるプラグイン
は現状存在しない。国内外での人気も高く、スニペットプラグインといえばこれという
存在である。
Python interpolation という独自の機能を持っている。これはスニペット展開の挙動を
Python スクリプトで制御できるというものであり、他のスニペットプラグインとは比較
にならないほど自由度が高い。
SnipMate ほどではないが最近の開発は停滞気味である。
高機能であるがゆえに他のプラグインと干渉がしやすい、
カーソルの監視のために大量の Python スクリプトを使うので、neovim だと RPC 通信
のオーバーヘッドが多く使用は推奨されない。

利点

  • 高機能

  • スニペット記述の自由度の高さ

  • Python interpolation のサポート

欠点

  • Python への依存

  • 開発は停滞

  • neovim では遅い

  • 機能が多すぎて習得に時間がかかる

  • 他のプラグインや設定との干渉

coc-snippets

coc フレームワークで構築されたスニペットプラグイン。
UltiSnips, SnipMate 両方の形式をパースすることができ、一見すると最強に見えるス
ニペットプラグインであるが、もちろんそんなことはない。
UltiSnips の機能が多すぎるので全ての機能に対応できないのだ。
内部的には Python の正規表現を JavaScript の正規表現に変換しているらしい。
issues を見ていると、UltiSnips に対する非互換性に対する苦情の数々が見える。

利点

  • SnipMate, UltiSnips 両方のスニペットファイルを読み込むことが可能

  • coc フレームワークの他のプラグインとの親和性が高い

  • 比較的高機能

欠点

  • coc フレームワークへの依存

  • UltiSnips との互換性は完璧ではない

  • プラグインの設定は JSON 形式

snippet.nvim

neovim 用のスニペットプラグインとして期待されている。Lua で記述されている。
全てのプラグインを Lua で統一したいならば候補に入るだろう。
スニペットの記述は Lua スクリプトでしなければならず、書くのが大変である。
一応他のスニペットのパーサーを用意する予定らしい。
スニペットの実現方法はマーカー式のようだ。

利点

  • neovim なら依存関係なしに導入可能

  • シンプルな機能

欠点

  • 独自のスニペットフォーマット

  • neovim への依存

  • 設定は Lua 形式

  • マーカー方式

deoppet.nvim

dark powered neo plugins シリーズの名を冠する neosnippet.vim の後継となるプラグ
インである。UltiSnips を越えるスニペットプラグインを目指して開発中となってい
る。

長らく実装が何もない vaporware だったが、その状態でなぜか 100stars 越えを達成す
るという謎の記録を残していた。
現在基本的な動作は実装済みで、ようやくスニペットプラグインとして使えるレベルに
なった。

deoppet.nvim は neosnippet.vim のスニペットファイルをそのまま読み込むことが可能
である。将来的には UltiSnips 形式等にも対応予定。

特徴としては neovim の extmark という機能を用いることにより、マーカーを使わなく
てもスニペットのタブストップの監視が行えるよう になったことである。よって、
neosnippet.vim にあったマーカー回りの欠点は存在しない。neosnippet.vim では行え
なかった後方へのジャンプもサポートしている。

利点

  • neosnippet.vim の欠点を解消

  • 高速な動作

欠点

  • まだ開発中で人に推奨できるレベルの完成度ではない

  • Python への依存

  • 最新版の neovim への依存、今後 Vim8 への対応予定

まとめ

deoppet.nvim に期待せよ、クックック……。

Discussion