vim-smartinput のスマートか分からない設定

vim-smartinput@kana さん作の Vim プラグインで,賢い入力補助を行なってくれます.

inoremap ( ()<Left>
inoremap { {}<Left>

とか設定している人には特にオススメです.
詳細については smartinput.txt を見ていただければ分かると思います.
インストールするだけでも多くの入力補助がデフォルトでオンになっており(また,既存のマッピングとかち合っておかしくならないように工夫されていて),そのままでもとても便利なプラグインです.

しかし,それだけではなく,このプラグインは高いカスタマイズ性も提供しており,自分で自由に入力補助のルールを追加できます.
僕が vim-smartinput で使っている設定を,メモがてら書いておきたいと思います.

括弧内でのスペース入力

call smartinput#map_to_trigger('i', '<Space>', '<Space>', '<Space>')
call smartinput#define_rule({
            \   'at'    : '(\%#)',
            \   'char'  : '<Space>',
            \   'input' : '<Space><Space><Left>',
            \   })

call smartinput#define_rule({
            \   'at'    : '( \%# )',
            \   'char'  : '<BS>',
            \   'input' : '<Del><BS>',
            \   })

カーソルが

(c)

という状態にあるとき(c はカーソル),インサートモードでスペースを入力すると

( c )

という感じにスペースがカーソルの左右両方に挿入されます.また,この状態でバックスペースを押すと左右のスペースが一気に消えます.
{} や [] や <> にも同じように設定しています.
ちなみに,smartinput#map_to_trigger() は入力補助のトリガーになるキーを追加する関数で一度追加すれば良く,デフォルトでトリガーになっているキーは追加する必要がありません.

改行時に行末スペースの除去

call smartinput#define_rule({
\   'at': '\s\+\%#',
\   'char': '<CR>',
\   'input': "<C-o>:call setline('.', substitute(getline('.'), '\\s\\+$', '', ''))<CR><CR>",
\   })

僕は行末の無駄なスペースは極力除去したい派なので,過激派の頃はファイル保存時に全行末スペースを削除していたりしたんですが,バージョン管理などで面倒くさいことになったりするので最近は改行入力時に除去するようにしています.

hoge<Space><Space>c

とあったときに( は空白),Enter を押すと

hoge
c

というふうに空白が取り除かれます.
なお,行末にスペースがあったときのみ 'input' に指定した処理が行われるため,普段の改行時に常に行末スペース除去の処理が走って改行がもっさりするようなことはありません.
これで

inoremap , ,<Space>

のように心置きなく設定できます.(通常だとコンマを入力したあとに改行してしまうと行末にスペースが残ってしまう)

C++ で ; を忘れないようにする

call smartinput#define_rule({
\   'at'       : '\%(\<struct\>\|\<class\>\|\<enum\>\)\s*\w\+.*\%#',
\   'char'     : '{',
\   'input'    : '{};<Left><Left>',
\   'filetype' : ['cpp'],
\   })

struct や class,enum の {} の後にはセミコロンが必要ですが忘れがちです.
そこで,struct や class,enum の宣言時のみセミコロンを追加します.

struct Hoge c

という状態で { を入力すると,

struct Hoge {c};

とセミコロンも入力され,ここで Enter を押すと

struct Hoge{
    c
};

というふうに良い感じに括弧が展開されます(括弧の展開はデフォルトの補助機能)

このルールはデフォルトの, { を入力すると {} が挿入されるルールを上書きしてしまうように見えますが,実際にはルールには優先度が設定されており,条件に合う複数のルールがある場合優先度の高いものが適用されるようになっているので大丈夫です.

C++ で boost:: や std:: の入力補助

書き捨てのコード以外で

using namespace std;

などしている人はいないと思いますが,真面目に boost:: や std:: を入力するのも結構面倒です(よく使う識別子は using std::cout; などするという方法もありますが…)
そこで,よく入力する名前空間 std:: や boost:: や detail:: を簡単に入力できないか考えます.

まず,僕はセミコロンとコロンのマッピングを入れ替えて使っているので,C++ 編集時は

inoremap ;; ::

というマッピングをすることにより,:: の入力をしやすくしていました.
そこで,smartinput で同様の設定をやり直します.

call smartinput#map_to_trigger('i', ';', ';', ';')
call smartinput#define_rule({
            \   'at'       : ';\%#',
            \   'char'     : ';',
            \   'input'    : '<BS>::',
            \   'filetype' : ['cpp'],
            \   })

わざわざ smartinput で設定し直すのは,smartinput は既存のマッピングを上書きしないようになっているため,上記の nnoremap の設定を残しておくと次に設定する入力補助が効かなくなるためです.

次に b;; や s;; や d;; と打つと boost:: や std:: や detail:: に展開するようにします.

" boost:: の補完
call smartinput#define_rule({
            \   'at'       : '\<b;\%#',
            \   'char'     : ';',
            \   'input'    : '<BS>oost::',
            \   'filetype' : ['cpp'],
            \   })
" std:: の補完
call smartinput#define_rule({
            \   'at'       : '\<s;\%#',
            \   'char'     : ';',
            \   'input'    : '<BS>td::',
            \   'filetype' : ['cpp'],
            \   })
" detail:: の補完
call smartinput#define_rule({
            \   'at'       : '\%(\s\|::\)d;\%#',
            \   'char'     : ';',
            \   'input'    : '<BS>etail::',
            \   'filetype' : ['cpp'],
            \   })

入力する b;; や s;; の直前は語境界になっていないといけないため,hogeb;; が hogeboost:: に展開されるといったような心配はありません.また,detail:: は hoge::detail:: のようにネストして書かれることが多いので,入力する d;; は :: に続いている場合を考慮に入れています.

Vim正規表現内のグループ化

Vim のグループ化は括弧をエスケープしないといけないため,やや入力が面倒になります.

call smartinput#define_rule({
            \   'at'       : '\\\%(\|%\|z\)\%#',
            \   'char'     : '(',
            \   'input'    : '(\)<Left><Left>',
            \   'filetype' : ['vim'],
            \   'syntax'   : ['String'],
            \   })

このルールにより,filetype が vim で,文字列内を編集している時に

hoge =~ 'c'

という状態で \( を入力すると

hoge =~ '\(c\)'

となります.\%(\) や \z(\) の補完も同様に出来ます.

また,削除時の設定も行います.

call smartinput#define_rule({
            \   'at'       : '\\(\%#\\)',
            \   'char'     : '<BS>',
            \   'input'    : '<Del><Del><BS><BS>',
            \   'filetype' : ['vim'],
            \   'syntax'   : ['String'],
            \   })
call smartinput#define_rule({
            \   'at'       : '\\[%z](\%#\\)',
            \   'char'     : '<BS>',
            \   'input'    : '<Del><Del><BS><BS><BS>',
            \   'filetype' : ['vim'],
            \   'syntax'   : ['String'],
            \   })

1つ目のルールで,

hoge =~ '\(c\)'

のときにバックスペースを押すだけで

hoge =~ 'c'

のようにグループ化前に戻れます.
2つ目のルールにより,\%(\) や \z(\) の削除も同様に行えます.

Ruby で文字列内展開 #{} やブロック引数 do || の補助

call smartinput#map_to_trigger('i', '#', '#', '#')
call smartinput#define_rule({
            \   'at'       : '\%#',
            \   'char'     : '#',
            \   'input'    : '#{}<Left>',
            \   'filetype' : ['ruby'],
            \   'syntax'   : ['Constant', 'Special'],
            \   })

call smartinput#map_to_trigger('i', '<Bar>', '<Bar>', '<Bar>')
call smartinput#define_rule({
            \   'at' : '\({\|\<do\>\)\s*\%#',
            \   'char' : '<Bar>',
            \   'input' : '<Bar><Bar><Left>',
            \   'filetype' : ['ruby'],
            \    })

これは vim-smartinput の help の examples にも載っている設定で

"hoge c"

で # を入力すると

"hoge #{c}"

になったり,

[1,2,3].map do c

で | を入力すると

[1,2,3].map do |c|

になったりします.