かなりすごいブログ

Vimと対話せよ

Vim Advent Calendar 2012、303日目の記事になります。

何やら来月にmomonga.vim #2というのが開催されるらしいですね。楽しみです(すごく)。

Vimと対話せよ

はい、Vimに対する不満の一つに、cmdlineがありますね。例えばEvilの置換実装、あれみたいなことができなかったりします。

EvilにあってVimにない機能もあります. たとえば, Evilでは置換の際にもパターンにマッチした部分をハイライトでき, さらに置換結果をバッファ内にプレビューできます. 20130209160344
引用:Evil: EmacsをVimのごとく使う – 導入編

こういう点に関しては、Emacsが羨ましかったりしますね。擬似的にcmdlineのようなものを作ってウィンドウ分割でごまかすくらいしか実装方法が思いつきません。つらい。

まぁそれはそれとして、cmdlineへの不満の一つとして、「もっと対話したい」というのもあります。そう、VimmerはもっとVimと対話したいのです。

Vimのコマンドは、基本的に対話を考えていない様に見えます。たとえば未保存のバッファで:qを実行すると警告が出るのみで、ユーザはもう一度最初から打ち直し、:q!を実行する必要があります。これは非常に不便です。:qを実行したユーザがそのバッファを閉じたいと思っているのは明らかなので、「未保存だけど、どうする?強制的にqしちゃう?」とVimに聞いて欲しいですね。

うん、こちらならなんとかなりそうです。というわけでなんとかしました(なんとか)。

Vim script(かなり)

function! SelectInteractive(question, candidates) " \{\{\{
  try
    let a:candidates[0] = toupper(a:candidates[0])
    let l:select = 0
    while index(a:candidates, l:select, 0, 1) == -1
      let l:select = input(a:question . ' [' . join(a:candidates, '/') . '] ')
      if l:select == ''
        let l:select = a:candidates[0]
      endif
    endwhile
    return tolower(l:select)
  finally
    redraw!
  endtry
endfunction " \}\}\}

function! BufferWipeoutInteractive() " \{\{\{
  if &modified == 1
    if SelectInteractive('Buffer is unsaved. Force quit?', ['n', 'y']) == 'y'
      bwipeout!
    endif
  else
    bwipeout
  endif
endfunction " \}\}\}

nnoremap <c-x>k  :call BufferWipeoutInteractive()<cr>

はい、これで<C-x>kにて対話的なバッファ削除を実装できました。私はバッファを綺麗サッパリ消したい派なのでbwipeoutを使っていますが、そうじゃない人はqとかでいいでしょう。

未保存のバッファを開いた状態でキーバインド<C-x>kを実行すると、「Buffer is unsaved. Force quit?」と質問されます。cmdlineでそのまま何も入力せずにEnterを押下すると、デフォルト選択肢(1つ目の選択肢)であるNが選択されたとみなされ、バッファ削除がキャンセルされます。yを入力すると、bwipeout!が実行され、強制的にバッファが削除されます。

いいかんじですね。でも、次のようにしたらもっとよさそうです。

Vim script(ふたつめの)

function! SelectInteractive(question, candidates) " \{\{\{
  try
    let a:candidates[0] = toupper(a:candidates[0])
    let l:select = 0
    while index(a:candidates, l:select, 0, 1) == -1
      let l:select = input(a:question . ' [' . join(a:candidates, '/') . '] ')
      if l:select == ''
        let l:select = a:candidates[0]
      endif
    endwhile
    return tolower(l:select)
  finally
    redraw!
  endtry
endfunction " \}\}\}

function! BufferWipeoutInteractive() " \{\{\{
  if &modified == 1
    let l:selected = SelectInteractive('Buffer is unsaved. What should I do?', ['n', 'w', 'q'])
    if l:selected == 'w'
      write
      bwipeout
    elseif l:selected == 'q'
      bwipeout!
    endif
  else
    bwipeout
  endif
endfunction " \}\}\}


nnoremap <c-x>k  :call BufferWipeoutInteractive()<cr>

はい、これで、Nもしくは未指定でキャンセル、wで書き込みを行ってからbwipeoutqで書き込みを行わずにbwipeout!が実行されるようになりました。とても便利ですね。

まとめ

cmdlineを利用して、もっとVimと対話していきましょう!