Vimの強力なスニペットプラグイン UltiSnips
UltiSnips is the ultimate solution for snippets in Vim. It has tons of features and is very fast.
UltiSnipsはVimのスニペット用の超強力なソリューションです。たくさんの機能を持ちながら、非常に高速です。
上のリンク先のアニメーションGIFを見るのが一番わかり易い 他にも設定の仕方とかが全部動画付きで説明されている非常にわかりやすい
入力した値をスクリプトで解析して整形した全然違う文字列に改変したりできる
日付の入力をスニペットにする、などスクリプトが生成できる文字列をそのままバッファ内に持ち込める
成果物
https://gyazo.com/b613d1733304b347377b11732327de84
ドキュメントについて
極論を言うと、Vimを開いて:h UltiSnipsしてヘルプを読む
だけだと僕も思い出せないので、設定の仕方についてわかった範囲でメモる
あまりこのプラグインを解説してる日本語記事がなくて、サッと調べてもよくわからず英語のヘルプを頑張って読んだ
スニペットファイルを作成する
ファイル名は、対応する拡張子名.snippetsにして、所定のディレクトリに配置する
snippetsシンタクスハイライトもプラグインが用意してくれてるので、拡張子を間違えてもすぐわかる
拡張子を調べるときはVimを開いて:echo glob($VIMRUNTIME . '/ftplugin/*.vim')
あるいは、vim hoge.mdみたいに拡張子指定でファイルを開いてから:set ftとコマンドを実行すると、現在のファイルの拡張子が何として開かれているか確認できる
マークダウンがmdではなくmarkdown.vimだったり、シェルもsh.vimだったりするのがわかる
所定のディレクトリはデフォルトだと~/.vim/UltiSnipsだと思う(たぶん)
僕は管理する場所を変更したかったのでlet g:UltiSnipsSnippetDirectories=["conf.plugins.d/ultisnips.snippets.d"]という設定を入れて~/.vim/conf.plugins.d/ultisnips.snippets.dにスニペットファイルをおいて管理してる
よってシェルのスニペットを~/.vim/conf.plugins.d/ultisnips.snippets.d/sh.snippetsとして作成した
書く
for文のスニペット
code:sh.snippets
snippet fori "for ((i=0; i<max; i++)) ..."
for ((${1:Counter}=0; $1<${2:Max}; $1++)); do
${3:Implementetion}
done
endsnippet
snippetの後ろにスニペット文字列、その後ろに説明文
snippet〜endsnippetの間はスニペットの世界。好きに書く
$1〜みたいな連番の特殊な変数は、スニペット入力直後のジャンプ可能なテキストが格納される
連番1から始まる。0始まりではない
基本この特殊変数を活用してスニペットを書く
${1:Description}と書くと、入力する箇所に「Description」という文字が表示される
一箇所${1:Description}のような記載をすると、それ移行の$1は明示的に指定しなければDescriptionが表示されるようになる
Pythonと連携するスニペット
code:sh.snippets
global !p
def parse_args(args):
varnames = []
for line in args.split("\n"):
if not "# - " in line:
continue
vs = line.strip(" \t#-").split(" ")
if 0 < len(vs):
if v == "":
continue
varnames.append(v)
ret = ""
for v in varnames:
ret += "local %s\n " % (v)
for i, v in enumerate(varnames):
ret += "%s=$%d\n " % (v, (i+1))
return ret.rstrip()
endglobal
snippet genf "関数を生成する"
# ${1:FunctionName} は${2:Description}
# - ${3:Arguments}
$1() {
!p snip.rv = parse_args("# - " + t[3])
${4:Implementetion}
}
endsnippet
外部依存を増やしたくなかったけれど、VimScript書けないマンなのでPythonで書いた。Pythonなら2も3も書ける 一番下のsnippet genfの箇所でparse_args(t[3])とpythonの関数を呼び出してるのが肝
global !p〜endglobalでPythonのコードを書ける。ここでは関数を定義
!pはpythonのコードを書くぞ、という宣言。!vだとVimScript
1行で書く!pはバッククォートで囲う必要がある
snip.rvに文字列を入れるとそれがVimのバッファに展開される
snip.rv = "Hello World"でHello Worldできる
ここでのparse_argsはテキスト入力の都度実行されて、t[3]の値が計算しなおされる
tという特殊な配列には$1とかと同じ値が入ってる。t[1] == $1と考えて良い
parse_argsでやってることは下記のとおり
code:bash
# こんな感じのドキュメントで下の変数宣言が作られる
# - x int
# - y string
func() {
local x
local y
x=$1
y=$2
}
特定の規則は
1. 「# - 」で始まって
2. 「変数名 適当な説明(説明はあってもなくてもいい)」という行である
以上の条件を満たす行の変数名を取得し、ローカル変数宣言に使用する
この条件にするためにparse_args("# - " + t[3])とかいう汚い呼び出し方にした
bashにはjavadocみたいなドキュメンテーションコメントは(たぶん)ないから、それっぽいものを自分で定義 あとはよくあるただのPythonでのテキスト処理なので特に書くことはない
bashの余談だが、local x=$1とかってやると、$1が空でもexit 0になる readonly x=$1とかも同じ現象が起きたはず
set -euオプションをつけると、未定義の変数を参照しようとした時点で弾かれるので安全