読者です 読者をやめる 読者になる 読者になる

try-catch の弊害

vim

Vim スクリプトでは try-catch が使えて非常に便利。

try
  echo 'start'
  throw 'error!'
  echo 'end'
catch /error!/
  echomsg 'error occurred.'
endtry
start
error occurred.

この try-catch、一般的な Vim のエラーが発生した場合も catch してくれる。

unlet! var
try
  echo 'start'
  echo var
  echoerr 'hoo'
  echo 'end'
catch
  echomsg 'error occurred:' . v:exception
endtry
start
error occurred:Vim(echo):E121: Undefined variable: var

なんて便利!かと思いきや、ここに大きな落とし穴がある。
それは、普通に実行していれば多少エラーが出ても実行される場合も try-catch があるとそこで止まってしまう、ということ。

例えば、新しいバッファを作ってカスタマイズのために FileType を設定するようなプラグインを書くとすると、

function! s:new_buf()
  new
  set filetype=plug
endfunction

function! s:start()
  try
    if !s:buf_exists()
      call s:new_buf()
    endif
    call s:load_contents()
  catch
  endtry
endfunction

この場合、set filetype=plug で FileType イベントが発生し、任意のコードが走る。そのなかのどこかで何かしらエラーが発生した場合、それ以降のコードが実行されず結果ただ真っ白なバッファが開かれることになってしまう。これは残念すぎる。
ちなみに try-catch がなければエラーこそ表示されるものの処理は先に進んで s:load_contents() が呼ばれる。
これを避けようと思ったら

  1. try-catch を一切使わない
  2. 予期しないエラーが発生しそうな箇所では逐一 try-catch で囲う
  3. なんとかしてロールバックして(この場合newしたバッファを閉じる)エラーを報告する

ということになると思うが、1 は try-catch は通常の処理時のエラーの処理をするのに便利だから使いたいし、かと言って 2 は面倒すぎる。3 もかなりむずい。
さて困った、どうしよう。と言うのが実は ref.vim の現状だったりする。と言うわけで今のところ FileType イベント中にエラー出ると残念なことになるのでよろしく。解決も難しそうだし仕様になるかもね…。