プログラムにせよ何にせよ、
( と )[ と ]{ と }' と '" と "等々、対応する文字を入力することはよくあります。
入力それ自体は難しいことではありませんが、
ペアで入力しなければ意味がないため、
場合によっては入力を忘れてしまうこともあります。
特にプログラムを書いているとこの手の入力漏れで構文エラーになることは多々あります。
例えば以下のような感じです:
ZapZapZap(Convert.ToInt32(e.Item.Cells[0].Text);
まあ何度も( や [ を入力していれば一個くらい対応する] や ) を入力し損ねるのは仕方がありません。
しかし入力し損ねる度にしょうもない構文エラーでコンパイルが失敗したりテストが失敗するのは士気に響きます。
もう2012年なのですから、どうにかして対応する括弧くらい自動で入力してもらえないものでしょうか。
vim-smartinput を使います。
ぶっちゃけこの手のプラグインは様々な先行実装が存在するのですが、
どれも拡張性に乏しかったりろくすっぽテストが書かれていなかったりと使う気になれなかったので、
自分で満足ができるものを作ろうとしました(つまりまだ不満が残っています; 後述)。
vim-smartinput をインストールするとデフォルトで以下のような入力補助が行われます
(以下の例で # はカーソル位置を表すものとします)。
初期状態 | 入力 | 結果
============================
# | ( | (#)
----------------------------
# | { | {#}
----------------------------
# | " | "#"
初期状態 | 入力 | 結果
============================
(#) | ) | ()#
----------------------------
[#] | ] | []#
----------------------------
"foo#" | " | "foo"#
初期状態 | 入力 | 結果
============================
(#) | <BS>| #
----------------------------
()# | <BS>| #
初期状態 | 入力 | 結果
============================
\# | " | \"#
----------------------------
\# | < | \<#
初期状態 | 入力 | 結果
============================
let# | 's | let's#
----------------------------
foo(# | ' | foo('#'
初期状態 | 入力 | 結果
============================
# | 'foo | 'foo#
----------------------------
"#" | 'hi | "'hi#'"
初期状態 | 入力 | 結果
=====================================
if (...) {#}| <Enter> | if (...) {
| | #
| | }
-------------------------------------
if (...) | <Enter> | if (...)
{#} | | {
| | #
| | }
先述の入力補助は序の口で、
vim-smartinput では必要に応じてどのように入力補助を行うか(まあ割と)自由に拡張できます。
入力補助の拡張や調整は「ルール」単位で行います
(先述の入力補助も単に標準でいくつかルールを定義しているだけです)。
ルールは
dictionary
(Vim 語; 他言語で言うところの連想配列的なもの)で表現され、
基本的に以下の3要素で構成されます:
at: ただの正規表現)char: キーを表す文字列)input: 代替入力を表す文字列)例えば
「( を入力したら代わりに () を入力してついでにカーソルを括弧の間に入れる」
は以下のルールで実現できます(\%# はカーソル位置を表す Vim 族専用の正規表現です):
{'at': '\%#', 'char': '(', 'input': '()<Left>'}
後は vimrc に
call smartinput#define_rule({'at': '\%#', 'char': '(', 'input': '()<Left>'})
等を追加してルールの定義を行えば新しいルールが有効になります。
やりましたね。
先程の
「( を入力したら代わりに () を入力してついでにカーソルを括弧の間に入れる」
というルール単体だけでは Perl 等で正規表現を書く際に困ります。( にマッチする正規表現を書こうと \( を入力すると代わりに \() が入力されてしまうからです。
なので正規表現の入力用に
「カーソルが \ の直後ならば ( 単品を入力する」
というルールを追加しましょう:
{'at': '\\\%#', 'char': '(', 'input': '('}
こうすると「( を入力した時に一体どちらのルールを適用すべきか」という問題が発生しますが、
実のところルール毎に
優先度
が定義されており、
優先度の高い順で
「ルールに合致する状況であればそのルールを適用する」
ことになっています。
基本的には「 at がやたら長いものはそれだけ複雑な文脈の指定になっている」という前提でat の長いルールの優先度が高くなるように設定されています。
上記の例の場合は \%# より \\\%# の方が明らかに長いので、
まず正規表現用のルールが試され、
その後に一般的なルールが試されることになります。
at の他に filetype だの syntax だのと追加で細かい文脈の指定ができるので、
それなりに愉快なルールが記述できるようになっています。
ルールの詳細は:help smartinput-rules
を、
ルールの具体例は
標準のルールの定義
を参照してください。
とまあ色々と持ち上げてみたものの、
実際のところ汎用的なルールを書くのは難しいものがあります。
. でのリピートが正確にできないなので我こそはと思う方はどうぞ改善してください。
次回は Emacs 編です。