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

複数行のerrorformatをちょっと勉強した

vim

TokyoVim#9に行ってきた - ぼっち勉強会でちょっと勉強した記録

エラーフォーマット自体のhelpはこれ。
:help errorformat

今回頑張っていたのは、複数行の対応。
:help errorformat-multi-line

準備

確認用のスクリプトはこれ。msgやformatをアレコレ変えて挙動を確認してた。
ちなみに元ネタはPHPUnitの結果をQuickfixに出すためのこの設定から。
vim-phpunit/compiler/phpunit.vim at master · afternoon/vim-phpunit · GitHub

function! s:test()
  let msg = [
        \ '1) SampleTest::Error',
        \ 'Failed asserting that two strings are equal.',
        \ 'expected:1',
        \ 'actual:2',
        \ '',
        \ '/Users/kanno/.vim_sandbox/2012-08-04-1329.vim in 5',
        \ ]
  try
    let errorformat = &g:errorformat
    let &g:errorformat = '%E%n)\ %.%#,%Z%f\ in\ %l,%C%m,%-G%.%#'
    cgetexpr msg
    cwindow
  finally
    let &g:errorformat = errorformat
  endtry
endfunction

call s:test()

これを実行してQuickfixに表示されるのはこんな感じ

/Users/kanno/.vim_sandbox/2012-08-04-1329.vim|5 error 1| Failed asserting that two strings are equal. expected:1 actual:2

formatの理解

let &g:errorformat = '%E%n)\ %.%#,%Z%f\ in\ %l,%C%m,%-G%.%#'

まず、エラーフォーマットはカンマ区切りで複数設定出来る。
なので、上記設定は以下の4つが設定されていることになる。

  • %E%n)\ %.%#
  • %Z%f\ in\ %l
  • %C%m
  • %-G%.%#

次に、エラーフォーマットは設定順に適用される。
なので疑似コードで書くならこういうことになる。

if("%E%n)\ %.%#" に一致) {
} else if("%Z%f\ in\ %l" に一致) {
} else if("%C%m" に一致) {
} else if("%-G%.%#" に一致) {
} else {
}

条件が厳しいものを先に書かないと、期待しない動作になるので設定順は大事。

そしたら各設定の意味を一つずつ見ていく。

%E%n)\ %.%#

%Eは「複数行エラーメッセージの開始」なので、
複数行からなるメッセージの最初の行パターンに付ける必要がある。
ここからエラーメッセージ始まってますよ、と教えてあげるわけだ。

で、%nはエラー番号、「\ 」で半角スペース、「%.%#」は正規表現でいう「.*」と同じ。

%Z%f\ in\ %l

%Zは「複数行エラーメッセージの終了」なので、
複数行からなるメッセージの最後の行パターンに付ける必要がある。
ここでエラーメッセージ終わってますよ、と教えてあげるわけだ。

で、%fはファイル名、「\ 」で半角スペース、%lはエラー行番号

%C%m

%Cは「エラーメッセージの継続」を表す。
さっき%Eで始まったと思うけどこれはその続きの行だからね、と教えてあげるわけだ。

で、%mはエラーメッセージを示す。文字列なら何でも入ってくるんだと思う
この場合、設定順はかならず%Eや%Zの後にすること。
%mで一致するパターンが広すぎるので、%Eや%Zとして検知したかったパターンも拾われてしまうので。

%-G%.%#

%-Gは「このメッセージを無視する」ことを表す。
quickfixに取り込む内容として不適切な場合に取り除く的なことだと思う。
「%.%#」は正規表現でいう「.*」と同じ。
設定順が最後なので、上記どれにも当てはまらない場合は無視するようにしているわけ。

流れに沿っておさらい

つまり上記例にあるこのメッセージはこんな感じで解釈される

1) SampleTest::Error
Failed asserting that two strings are equal.
expected:1
actual:2

/Users/kanno/.vim_sandbox/2012-08-04-1329.vim in 5

  • 「1) SampleTest::Error」に対してパターン一致
    • 「%E%n)\ %.%#」に一致
      • 「%E」により複数行のエラーメッセージとして認識開始
  • 「Failed asserting that two strings are equal.」に対してパターン一致
    • 「%E%n)\ %.%#」「%Z%f\ in\ %l」と順に当てはめていくが一致しない
    • 「%C%m」に一致
      • 「%C」により複数行のエラーメッセージとして認識
  • 「expected:1」に対してパターン一致
    • 同上
  • 「actual:2」に対してパターン一致
    • 同上
  • 「」(空行)に対してパターン一致
    • 「%E%n)\ %.%#」「%Z%f\ in\ %l」「%C%m」と順に当てはめていくが一致しない(%mは空行に一致しないっぽい)
    • 「%-G%.%#」に一致(%.%#が何でも引っかかるので)
      • 「%-G」により無視する行だと認識
  • 「/Users/kanno/.vim_sandbox/2012-08-04-1329.vim in 5」に対してパターン一致
    • 「%E%n)\ %.%#」と順に当てはめていくが一致しない
    • 「%Z%f\ in\ %l」に一致
      • 「%Z」により複数行のエラーメッセージの終行だと認識して終わり

きっとこういうことなんだと思う。