Vim で if endif とか対応するキーワードを強調表示する
昨日の続き
はじめに
カーソルが if とか対応するものが存在するキーワード上にあるとき、endif とか対応するキーワードを強調表示させる。この機能も多分大体の IDE にはあると思う。
これも Vim でコード読むときあったら便利な気がしたので調べた。
これを実現するための要素
- カーソルの下の単語が if とか for とか対応するワードが存在するものかどうかの判別
- 対応するキーワードの取得 (できればネストの考慮)
他、前回の内容
コード
実は "matchit highlight" でググれば同じようなことをしている人が見つかった。
(リンクはっていいか不明なので省略、ググってください)
それを参考にしつつ、記述したのが以下のコード
function! s:GetMatchitPattern(cwd) if !exists('b:match_words') return '' endif if !exists('b:mw') call s:InitMatchit() endif for pat in b:mw if a:cwd =~# pat let l:cmw = pat break endif endfor if !exists("l:cmw") return '' endif let lcs = [] let wsv = winsaveview() while 1 exe 'normal %' let lc = {'line': line('.'), 'col': col('.')} if len(lcs) > 0 if (lc.line == lcs[0].line && lc.col == lcs[0].col) || (lc.line == lcs[-1].line && lc.col == lcs[-1].col) break endif endif call add(lcs, lc) endwhile call winrestview(wsv) call map(lcs, '"\\%" . v:val.line . "l\\%" . v:val.col . "c"') let lcre = join(lcs, '\|') return '.*\%(' . lcre . '\).*\&' . b:mwre endfunction function! s:InitMatchit() if !exists('b:match_words') return endif let b:mw = split(b:match_words, ',\|:') let l:mw = filter(b:mw, 'v:val !~ "^[(){}[\\]]$"') let mwre = '\%(' . join(l:mw, '\|') . '\)' let b:mwre = substitute(mwre, "'", "''", 'g') endfunction
(ほとんど参考元と同じ)
matchit.vim を有効にしていないと動作しません
上記の s:GetMatchitPattern の戻り値を前回同様 matchadd で指定してやれば、if, else, endif などが強調表示される。
内容メモ
ポイントは以下
- b:match_words
- マッチング用の正規表現に行番号と列番号を使用
- winsaveview() と exe 'normal %', winrestview()
b:match_words
b:match_words は matchit.vim プラグインが ( や { 以外の言語特有の対応パターンを認識するのに使用している変数。
file type のプラグインなどで、 <開始パターン>[:<中間パターン>]:<終了パターン>[,他パターン...] の形式で定義されている。
例) filetype=vim の場合(一部抜粋)
\<fu\%[nction]\>:\<retu\%[rn]\>:\<endf\%[unction]\>,\<\(wh\%[ile]\|for\)\>:\<brea\%[k]\>:\<con\%[tinue]\>:\<end\(w\%[hile]\|fo\%[r]\)\>
このパターンを使用して、現在のカーソル下の単語が特殊なキーワードかどうか判別できる
その場合は、split() を使用して、',' と '|' で分割してやればよい
マッチング用の正規表現に行番号と列番号を使用
例えば、"\%100l" のように指定するとそのファイルの 100 行目がマッチング対象となる。
さらに、"\%100l\%2c" と指定すれば、ファイルの 100 行目の 2 文字目がマッチング対象となる。
後は、その文字の後ろに b:match_words から取り出した単語をマッチング対象として加えれば良い。
winsaveview() と exe 'normal %', winrestview()
簡単に説明すると、
1. winsaveview() で現在の画面の状態を保存
2. exe 'normal %' で '%' を押したのと同じ状態にする (この後、現在の行番号と列番号を取得)
3. winrestview() で最初に保存した画面の状態を復元
関連する help
- b:match_words
- len()
- add()
- exists()
- line()
- col()
- map()
- filter()
- split()
- join()
- execute
- winsaveview()
- winrestview()