basyura's blog

あしたになったらほんきだす。

def に対応する end に移動する(またその逆)の試作

:help searchpairpos

searchpairpos({start}, {middle}, {end} [, {flags} [, {skip}
        [, {stopline} [, {timeout}]]]])
    |searchpair()|と同様だが、マッチの行番号と桁番号からなるリスト
    |List|を返す。このリストの最初の要素は行番号で、次の要素はマッ
    チの桁位置のバイトインデックスである。マッチが見つからなかった
    場合は[0, 0]を返す。 >
      :let [lnum,col] = searchpairpos('{', '', '}', 'n')
    より大規模で役に立つ例に関しては|match-parens|を参照。

このあたりを使えば入れ子も考慮して見つけられるらしい。
ただ、Ruby は if に対応するのも end だし、do に対応するのも end なのだ。何か解決できる関数があるのかもしれないけど、synIDattr を使って検索するようにしてみた。30 分ぐらいのやっつけ。

command! Pair :call s:search_pair()
function! s:search_pair()
  let word = expand('<cWORD>')
  if word == 'def'
    let flg = 'n'
  elseif word == 'end'
    let flg = 'b'
  else
    return
  endif
  while (line('.') != 0 || line('.') != line('$'))
    let pos = searchpairpos('\s\{}\zsdef\ze', '', '\s\{}\zsend\ze', flg)
    if pos == [0,0]
      let line = line('.') + (flg == 'n' ? 1 : -1)
      call cursor(line,col('.'))
      continue
    endif
    call cursor(pos[0],pos[1])
    let attr = synIDattr(synID(line('.'),col('.'),1),'name') 
    if attr == 'rubyDefine'
      break
    endif
  endwhile
endfunction

他言語でも使えるように汎用的にするのは無理ぽいんだけど・・・。