sandwich.vim を書きました

vim-sandwich という vim plugin を書きました。これは vim-surroundvim-operator-surround などのような、括弧やクオーテーションなどで囲まれた文字列を編集するためのプラグインと同種のものです。改行の扱いや矩形選択に対する挙動など、細かい点が自分好みなものが欲しくなったので書きました。

実のところ先駆者達と大きな違いありません。あえて挙げるとすれば、ドットリピートに外部ライブラリ(vim-repeat)を必要としない点、改行を含むパターンを扱えることなどが vim-surround と異なります。 vim-operator-surround との違いは補助用のテキストオブジェクトを同梱している点です。抱き合わせですね。ただ、補助用とは書きましたが単体でも使用できます。 vim-textobj-multiblockvim-textobj-anyblockvim-textobj-between (こちらは少し違う機能ですが)を想像していただければおよそそのままかと思います。このようなオペレータとテキストオブジェクトの必要とする情報/設定が似通ってきたので、まとめて管理したいと思ったのも書くきっかけでした。

オペレータは三種あります。それぞれ、囲みの追加/削除/置換を行います。削除あるいは置換される文字列は両端が同じ文字か、事前に設定された文字列のペア(例えば、 ())に一致した場合に編集されます。これは vim-operator-surround の定めているルールと同じです。

テキストオブジェクトは二種あります。一つはユーザーに入力を促し、入力に一致する設定があれば事前に設定されたペアに囲まれた領域を、一致するものがなければ入力された一文字に囲まれた領域を選択します。もう一つは事前に設定されたペアの中からカーソルに最も近いものを自動的に検索します。

囲みの削除/置換を行うオペレータはその性質上、補助用のテキストオブジェクトと常に一緒に使うのが便利なのでこれらの複合マッピングにバインドされています。まとめると操作は次のようになります。

  • 囲みを追加

sa にバインドされています。例えば saiw( と押下すると foo(foo) となります。

  • 囲みを削除

sd にバインドされています。例えば sd( と押下すると (foo)foo となります。また、 sdb と押下すると自動で検索するテキストオブジェクトが使われ、同じ結果が得られるでしょう。

  • 囲みを置換

sr にバインドされています。例えば sr(" と押下すると (foo)"foo" となります。また、上記と同様に srb" でも同じ結果を得られるでしょう。

  • テキストオブジェクト

ユーザーの入力を促すテキストオブジェクトは is 及び as に、自動で検索するテキストオブジェクトは ib 及び ab にバインドされています。 is 及び ib は囲みの内側のテキストを、 as 及び ab は囲みを含めた領域を選択します。

             |<----ib,is---->|
{surrounding}{surrounded text}{surrounding}
|<------------------ab,as---------------->|

いくつか特徴的な点を紹介します。

ハイライトについて

まず目に付くのがハイライト機能でしょう。囲みの追加/削除/置換において、挿入位置/削除される文字列/置換される文字列をハイライトします。囲みの削除においては削除の寸前に、ごく短い時間だけハイライトします。この時間は g:operator#sandwich#highlight_duration 変数で調節可能です。ハイライトの間もユーザーの入力をブロックせず、入力があれば即座に編集を終了します。しかし、それでもうっとうしいという方は次の行を vimrc に加えましょう。

 call operator#sandwich#set('delete', 'all', 'highlight', 0)

カウントについて

次にノーマルモードにおけるカウントの扱いが独特となっています。一般的にオペレータとモーションあるいはテキストオブジェクトの組み合わせは次のような順番で入力されます。

        [count1]{operator}[count2]{textobject}

通常のオペレータは [count1][count2] を区別しません。すなわち 3diwd3iw は全く等価です。両方を使った場合、そのカウントは二つのカウントの積になります。すなわち 2d3iw3d2iw は等しく、 6diw または d6iw にもまた等しくなります。これに対し、 sandwich.vim のオペレータは二者を区別します。 [count1] をオペレータに渡し、 [count2] をテキストオブジェクトへ渡します。すなわち 2sa3iw'' は二つの単語と間の空白を二度シングルクオーテーションで囲みます。2以上のカウントをオペレータに与えた場合デフォルトではカウントの回数だけユーザーに入力を促します。この挙動はオプションで制御可能です。

 call operator#sandwich#set('all', 'all', 'query_once', 0)

. コマンドで繰り返す際にカウントを与えた際には、カウントをテキストオブジェクトに渡し、オペレータは最後に与えられたカウントを使い続けます。

カスタマイズについて

さらに、 sandwich.vim には豊富なオプションがあります。カスタマイズをしようと思った時には大きな助けとなるでしょう。しかし、反面ドキュメントがそのために長くなっています。このためファイルを三つに分けています。 doc/sandwich.jax には共通部分について書かれており、これを眺めるだけでも使用には困らないでしょう。カスタマイズをしたくなったら doc/operator-sandwich.jax 及び doc/textobj-sandwich.jax をご覧ください。それぞれ分割してなお分量がありますが、 Vim にお詳しい方なら目次を眺めるだけでも何ができるかの見当がある程度つくかもしれません。


sandwich.vim