event happen or not ?

前々から空白文字を可視化する plugin ( http://github.com/januswel/dotfiles/blob/master/.vim/plugin/ShowInvisibleCharacters.vim ) を自前で書いて使っていたんだけど適用されない場合があるなと思って調べて直してみた。原因は autocmd で指定する event が間違っていたことだったんだけど今回改めて event まわり調べてみて罠が多いなーと思ったのでメモ。とりあえず BufNew と BufRead を ( 漠然と ) 指定していたのでその調査から。

  • BufNew
    • :help BufNew
    • 新しい buffer が作られた直後と buffer が rename された直後に発生する。 buffer が buffer list に登録されるときには BufAdd も同時に発生する。注意: autocmd が実行されているとき、現在の buffer を示す "%" は新しく作成された buffer を示す <afile> とは違うものを指しているかもしれない。
    • 要は buffer が新しく作られたときに必ず飛ぶけど autocmd に書いた command の効果が及ぶ先が新しい buffer ではない場合もあるよ、と。つーことで buffer が新しく作られたときに何かしたいという場合にこれを使えば無問題、というわけではないらしい。
    • autocmd に指定する command 内には '<afile>' ( 新しく作られた buffer の filename ) や '<abuf>' ( 同様に新しく作られた buffer の buffer number ) を指定することも出来る ( '%' と同じ context で展開されるので file 名を受け付ける command の引数として使う以外では expand() 関数で明示的に評価してやらないといけない ) けど <afile> は何も file を読んでない buffer では使えないし buffer number を指定できる command や関数があるわけではないようなので ( autocmd BufNew * bufdo if bufnr('') == expand('<abuf>') | call MyFunc() | endif っていう小細工もやってみたけど意図通りに動かず ) 確実に何かやらせたい場合はそもそも使わない方がいいと判断。
  • BufRead
    • :help BufRead
    • 新しく buffer を編集し始めるとき、 buffer に file が読み込まれたあとかつ modeline が実行される前に発生する。 modeline を解釈したあとに何かしたい場合は BufWinEnter を参照。これは ":r file" という command に対しては働か「ない」。 file が存在しない場合にも使われない。 file の復元 ( recovering と書いてあるので多分 :recover の結果のことを指していると思われる ) に成功したときに発生する。
    • というわけなので file 指定なしで buffer を作った場合 ( たとえば <C-w>n とか :tabnew とか ) にはそもそも発生しないわけだ。

さて、で今回はすべての buffer に必ず 1 回適用させたい処理がある ( 既存 file を読み込んだ場合でも存在しない file 名を指定した場合でも空の buffer を作った場合でも tab character や full-width space を可視化したい ) ので上記 2 つの event は使えない、と。そこでつらつら autocmd に使える event の一覧 ( :help autocommand-events ) を見ていったんだけどなんか BufWinEnter 使えそうじゃね ? と思ったので調べてみる。

  • BufWinEnter
    • :help BufWinEnter
    • buffer が window に表示されたあとに発生する。これは、 buffer が読み込まれたとき ( modeline が処理されたあと ) もしくは hidden buffer が window に表示されたとき ( つまり hidden buffer でなくなったとき ) に発生する。存在する buffer を再利用しているので、引数指定なしの :split ( 同じ buffer を違う window で編集している ) もしくはすでに window に開いている file を指定した ":split" に対しては発生しない。しかし現在の buffer 名を指定しての ":split" では buffer の再読み込みが行われるため発生する。
    • ということなので file を指定する / しないに関わらずその buffer が window に attach された瞬間に発生するみたいなので事実上見えるようになった buffer はすべてこいつが発生している、ということになる。 hidden buffer か否か、ということも余計に autocmd が飛ぶというだけなので多少の overhead はあるものの問題は解決するはず。というか hidden buffer を使うには 'hidden' が on になってなければならないみたいなんだけど ( :help buffer-hidden ) :set hidden? してみると "nohidden" なので別に問題なかった ( 少なくとも今は、ということだけど tabpage 使ってるし hidden buffer のうまみが正直わからんので多分 set hidden することはないと思う ) 。

でまぁここまで調べたところで google の精に http://vim-users.jp/2009/07/hack40/ てのがあるよと教えてもらったので ColorScheme event も同時に指定。 :colorscheme の存在は忘れていたというか自分で使うことがない command なのでありがたくぱくらせて使わせていただく感じで。ちなみにこの page で紹介されている code だと buffer をうつるたびに autocmd が飛ぶので overhead を気にする場合はあんまり良くない感じ。あと個人的に一文字のために encoding 指定がいるというのもイヤなので full-width space の指定は Unicode code point を使って /\%u3000/ としてる。んだけどこれ set encoding=utf-8 しているならという制限がつくことに今気付いた。んー :scriptencoding 指定した方が汎用性があるか。

というわけでとりあえず以下のように直したら意図に反することがなくなった。

http://github.com/januswel/dotfiles/commit/e7ca2d05790a6aeab73b0798171997205a628bca