Firefoxとかタブ付きエディタによくある最後のタブを閉じても終了せずに新規ファイルを編集するタブが開かれる機能がVimにもずっと欲しかった(特にgVim)。なるべく閉じないように使うことにしていたが、よく:wqaとか勢いで打ってしまい悲しいことに。そろそろどうにかしようと色々調べて、:cabbrevコマンドで:qとかを書き換えてやれば良さそうということがわかった。

:cabbrevでのビルトイン・コマンドの置き換えはReplace a builtin command using cabbrevで知った。コマンドの種類を取得するgetcmdtype()とコマンドの開始位置を取得するgetcmdpos()を使って、割り当てがコマンドの途中にでてきたりした場合に展開されないようにする。

現在のウィンドウを新規ファイルを編集する状態にするには:enewで良いので、:qa:wqaの場合は:tabonly:onlyに続けて実行してやれば良い。:q:wqの場合はウィンドウの数やタブページの数を調べて、最後の1つだった時だけ:enewにすり替えてやれば良い。ウィンドウの数を取得するには、ウィンドウ番号を取得するwinnr()$という引数を渡す。そうすると最後のウィンドウ番号、つまりウィンドウの数が取得できる。tabpagenr()も同じ。

" Don't exit vim when closing last tab with :q and :wq, :qa, :wqa
cabbrev q   <C-r>=(getcmdtype() == ':' && getcmdpos() == 1 && tabpagenr('$') == 1 && winnr('$') == 1 ? 'enew' : 'q')<CR>
cabbrev wq  <C-r>=(getcmdtype() == ':' && getcmdpos() == 1 && tabpagenr('$') == 1 && winnr('$') == 1 ? 'w\|enew' : 'wq')<CR>
cabbrev qa  <C-r>=(getcmdtype() == ':' && getcmdpos() == 1 ? 'tabonly\|only\|enew' : 'qa')<CR>
cabbrev wqa <C-r>=(getcmdtype() == ':' && getcmdpos() == 1 ? 'wa\|tabonly\|only\|enew' : 'wqa')<CR>

これだと:qa!とかが使えなくなるので、ちゃんと!を解釈できる関数を作ってやらないとならない……けど面倒なので。他にも副作用が多そうだ。

Vimを終了したい時は、ウィンドウの閉じるボタンを押したり、:x:xaまたはquit等の省略していないコマンドを使えば閉じることができる。