Vim Hack #40: 不可視文字を表示する の補足

以下の記事を読んで、やはり補足があった方がいいかな、と。

event happen or not ? - KBDAHOLIC - やぬすさんとこ

前置き

http://vim-users.jp/2009/07/hack40/ では :match を使った方法のみ解説したが、記事でも述べている通り :syntax を使った方法もある。
ではなぜこの方法の紹介を省いたかというと、複雑な上に完全にハイライトを行うのが難しいからだ。
はっきり言って偉そうに解説できるほど私もよくわかっていない。なので突っ込み等あればよろしくお願いします。

:syntax は実行された回数だけ定義される

まず、:syntax はバッファローカルで動作するコマンドだ*1。そして厄介なことに、実行した回数だけ定義が繰り返される。多重定義しても動作はするが、Vim の処理速度に影響が出そうだし、何より気持ちが悪い。
上の記事だと、例えば 'hidden' は使わないとしているが、'hidden' 状態にするには 'bufhidden' や :hide コマンドなど、他にも方法がある。そして使っているプラグインがこれらの機能を使わない保証はない。その場合に多重定義されてしまう恐れがある。

:syntax の再設定

:edit や :setfiletype などで syntax が再設定される際、そのバッファの syntax は全てクリアされる。もちろん自分で追加定義したものも例外ではない。その場合は当然再設定してやる必要がある。
:edit では BufWinEnter イベントも発生するので特に問題はないが、ファイルタイプが変更された際には BufWinEnter は発生しない。なので、Syntax イベントの場合にも :syntax の定義が必要になる。
ところが :edit 時には Syntax も BufWinEnter も両方発生する。つまり、何もしないと同じ :syntax が2重に定義される。これを回避しようと思ったら buffer-local な変数などで抑制するしかない。

さらに、:syntax enable などとしようものならどうしようもなくなる。
Syntax イベントには syntax を再定義するためのイベントが登録されているのだが、:syntax enable をするとこのイベントが一旦消されて再定義される。結果、自分で定義した Syntax 用のイベントよりシステムのイベントが後に定義される。イベントは定義された順に実行される。そしてこのシステムのイベントは、最初に syntax を clear する…。
これはもう vimrc 以外では :syntax enable しないことにしないと避けようがない。

原因不明の問題

まだ問題はある。:syntax include (:help :syn-include) で取り込まれた syntax のトップレベル以外では containedin=ALL で定義された syntax が反映されない。これはちょっとどういうことなのか私もよくわかってないが、とにかく少なくとも私の環境では反映されない。
これはつまり、以下のような個所では色が付かない。

<html><head>
<script language="javascript">
// ここは html の syntax で javascript を :syntax include で取り込んだ場所。ここの…
//   <- こことか
var hoge = "  <- ここ";
</script></head></html>

help を読んだ限りだと色が付いても良さそうなんだけど…よくわからないので原因や回避方法をご存知の方、居たら教えて頂けると嬉しいです。

以上のもろもろを踏まえると

多少のイレギュラーを無視するならともかく、厳密にハイライトしようとするとどうしても無理が出てくる。
何やら難しいことに頭を悩ますくらいなら :match を使ってしまえばいい。:match を使えば上記の問題は全部解決する。残るのは Window 移動するたびにコマンドが実行されて overhead が気になるくらいだけど、上記の問題に比べたら微々たるものだと私は思う。
もちろん、普段使ってる分には気付きにくい問題ではあるので、気にしないことにして :syntax の方を使うというのならそれはそれでありだとは思うけど。あまりお勧めはしない。

ちなみに highlight について

syntax がバッファ毎なのに対して highlight はグローバルなので、毎度設定する必要はない。必要があるのは highlight がクリアされるタイミング、つまり ColorScheme イベントが発生した時だけでいい。

*1:正確には on などのコマンドもあるが、ここでは syntax を定義するためのコマンドとして扱う