どうも、新年あけましておめでとうございます。今年は12エントリくらいを目指したいと思っております。あにゃきちです。
今日は久しぶりにvimのプラグインとか書いたので、その紹介とか少し。
件のプラグインはタイトルの通り textobj-xbrackets.vim です。ソースはとりあえずここ。使用には textobj-user.vim をインストールしておく必要があります。
で、xbrackets というのは、要するに x() とか x<> とか x[] とか x{} とかのことです。例えば、func(arg) というのとか、array[i] というのとか、sub { ... } というのとかに対応するテキストオブジェクトです。以下、少し動作の例(| はカーソル)。
printf("%f\n", (s|qrt(f) + 1));
daxb -> printf("%f\n", ( + 1));
dix( -> printf("%f\n", (sqrt() + 1));
2dixb -> printf();
i = array[0|] + array[1];
dax[ -> i = + array[1];
dix[ -> i = array[] + array[1];
PATH=~/bin:${PA|TH}
dav -> PATH=~/bin:
diV{ -> PATH=~/bin:${}
prog: $|(OBJ)
daV( -> prog:
div -> prog: $()
だいたい何かが前置されたカッコにマッチするオブジェクトということです。av, iv は ${} と $() のどちらにもマッチするように調整してあります。
x がマッチするのは、内部的には 'iskeyword' の並びです。わかりやすく言うと、開きカッコから b で 1 単語戻った場所をスタートとします。ただし、b で 1 単語戻った場所が 'iskeyword' に適合しなかった場合はスキップします(count を使っている場合、ただのカッコにはマッチしない)。
実際のところ x() は多くの言語にとっての関数呼び出しに相当しますが、前述の通り 'iskeyword' を判断の基準にしていますので厳密に関数呼び出しシーケンスには対応しません。関数の先頭の文字が数字であることを許容しない言語も多いですが、x() はそれであってもマッチします。
逆に言うと言語によらずマッチするものが明らかなので、vim の使用者が何が起こるかを考えて使用する限り、ユニバーサルに使うことが可能です(ちなみにこういう仕様にしたのは、関数だけにマッチを特定しようとしても、言語ごとのキーワードを覚えていない限り if (cond) などにマッチするのを防ぐことができず、そんなのは面倒だからです)。
次の例です。
sta|tic int func(int);
daxsb -> ;
dixs( -> static int func();
static char *f|unc2(void *);
daxsb -> static char *;
dixsb -> static char *func2();
daysb -> ;
diysb -> static char *func2()
axs, ixs は、前置する複数の 'iskeyword' 単語の並びにマッチします。マッチが終了するのは 'iskeyword' でない文字が現れたときか空行がある場合です。一方 ays, iys は 'iskeyword' に登録されていない * も含めてマッチします。これは実は g:textobj_xbrackets_extra_iskeyword = '*' という設定が(デフォルトで)してあるためで、y は一時的にこの文字を 'iskeyword' に加えた状態で検索を行います。
バッファごとに設定できる b:textobj_xbrackets_extra_iskeyword があれば設定を使い、なければグローバルな g:textobj_xbrackets_extra_iskeyword の設定を使います。例えば上述の C 言語の関数定義のように、'iskeyword' のマッチだけでは不満な場合に単語の拡大解釈を行えます。
最後の例。
/* thread function */
static void *
xxx_thread(void *arg)
{
...
return NUL|L;
}
/* next function */
days){ ->
/* thread function */
/* next function */
diys){
/* thread function */
static void *
xxx_thread(void *arg)
{
}
/* next function */
最後の例は C の関数の定義です。閉じカッコから始まるシーケンスは、次に連続する開きカッコを要求します。ys シーケンスのあと丸カッコが来て、さらにブレースで囲まれているようなオブジェクトにマッチします。inner として認識されるのは最後のカッコです。
ちなみにこのプラグイン自体は、「カッコの対応を探す部分」と、「その前後が規定されたシーケンスにマッチするか判別する部分」に分離されており、後者を自分で書けば新しいオブジェクトを簡単に定義可能です(前置ではなく後置を検出する関数を定義すれば、()x に対するオブジェクトも定義できる)。
また、ays){ のようなマッピングになるとかなり長いので、よく使いそうなものは
もっと短いマッピングに設定しても良いでしょう。なお、x() については既に a9, i9 にマッピングされています。
" function declarations
omap ad <Plug>(textobj-xbrackets-ys(_)-a)
xmap ad <Plug>(textobj-xbrackets-ys(_)-a)
omap id <Plug>(textobj-xbrackets-ys(_)-i)
xmap id <Plug>(textobj-xbrackets-ys(_)-i)
" function definitions
omap aD <Plug>(textobj-xbrackets-ys(){_}-a)
xmap aD <Plug>(textobj-xbrackets-ys(){_}-a)
omap iD <Plug>(textobj-xbrackets-ys(){_}-i)
xmap iD <Plug>(textobj-xbrackets-ys(){_}-i)
surround.vim っていうプラグインがあってそれはまあ便利なのですが、使っているうちに不満に思うことがやっぱりあって、なんか時間があるときについカッとなって別のものに直してしまう。そんなこと、ありますよね?最初に僕がこいつを直そうと思ったのは、cs の挙動が明かに変だからでした。まずはオリジナルのこの挙動を見てください(カーソルは |)。 if (cond) { | ... } cs}] if (cond) [| ... ] Crazy!! なんだこれは!
Tracked: 1月 02, 16:28