vimにおけるphpの関数移動について

vimで関数移動といえば

[[

もしくは

]]

ですね。
関数の前方または後方に一発ジャンプしてくれる便利な奴。

ただfiletypeがphpの時はなぜか挙動が違うくて、前方または後方の関数宣言にジャンプしてしまう。
この動きに関しては特に不満はないんやけど、
困ったことに検索パターンのレジスタ(@/)を関数宣言用の正規表現で上書きしやがる。
これはひっじょ〜〜〜〜〜にウザい。
しかも

nnoremap [[ :<C-u>call search('function', 'b')<CR>

とかやっても効いてくれんっていう。
今日はこれを解決するために動き出す。

調査

まずは調査。
本来の動作と違うのでどっかでmapしてるやろうと。

:verbose map [[

f:id:saihoooooooo:20111214224658p:image

ふむふむ。ftpluginか、なるほど。
なんかほぼ答えが見えてるけど、とりあえず追跡してみる。

:e $VIMRUNTIME/ftplugin/php.php

" Section jumping: [[ and ]] provided by Antony Scriven <adscriven at gmail dot com>
let s:function = '\(abstract\s\+\|final\s\+\|private\s\+\|protected\s\+\|public\s\+\|static\s\+\)*function'
let s:class = '\(abstract\s\+\|final\s\+\)*class'
let s:interface = 'interface'
let s:section = '\(.*\%#\)\@!\_^\s*\zs\('.s:function.'\|'.s:class.'\|'.s:interface.'\)'
exe 'nno <buffer> <silent> [[ ?' . escape(s:section, '|') . '?<CR>:nohls<CR>'
exe 'nno <buffer> <silent> ]] /' . escape(s:section, '|') . '/<CR>:nohls<CR>'
exe 'ono <buffer> <silent> [[ ?' . escape(s:section, '|') . '?<CR>:nohls<CR>'
exe 'ono <buffer> <silent> ]] /' . escape(s:section, '|') . '/<CR>:nohls<CR>'

てわけで普通に?と/でやってました。そら上書きされるわ。
しかもなんでただキーマップを書いても効かんと。
まぁ言語ごとに関数定義の正規表現は違うんでは適切かと思いますが。

対応策

原因がわかったところで対応開始。
かなりやっつけですが、以下を~/.vimrcに記述。

augroup MyAutoCmd
    autocmd!
augroup END

" phpにてlast-patternを変更しない関数移動を再マップ
autocmd MyAutoCmd FileType php call <SID>RemapPHPSectionJump()
function! s:RemapPHPSectionJump()
    let l:function = '\(abstract\s\+\|final\s\+\|private\s\+\|protected\s\+\|public\s\+\|static\s\+\)*function'
    let class = '\(abstract\s\+\|final\s\+\)*class'
    let interface = 'interface'
    let section = '\(.*\%#\)\@!\_^\s*\zs\('.function.'\|'.class.'\|'.interface.'\)'
    exe "nno <buffer> <silent> [[ :call search('" . escape(section, '|') . "', 'b')<CR>"
    exe "nno <buffer> <silent> ]] :call search('" . escape(section, '|') . "')<CR>"
    exe "ono <buffer> <silent> [[ :call search('" . escape(section, '|') . "', 'b')<CR>"
    exe "ono <buffer> <silent> ]] :call search('" . escape(section, '|') . "')<CR>"
endfunction

自分は一元管理したいので~/.vimrcに書いたけど、本来は~/.vim/ftplugin/php.vim なんかな。
とりあえずはスッキリ、ノーストレスで良い感じになりました。
同じ現象で困っている方は是非ご賞味ください。
副作用でたらゴメンナサイ。

補足

ここでpythonのftpluginをチラ見してみる。

:e $VIMRUNTIME/ftplugin/python.vim

nnoremap <silent> <buffer> ]] :call <SID>Python_jump('/^\(class\\|def\)')<cr>
nnoremap <silent> <buffer> [[ :call <SID>Python_jump('?^\(class\\|def\)')<cr>
nnoremap <silent> <buffer> ]m :call <SID>Python_jump('/^\s*\(class\\|def\)')<cr>
nnoremap <silent> <buffer> [m :call <SID>Python_jump('?^\s*\(class\\|def\)')<cr>

if exists('*<SID>Python_jump') | finish | endif

fun! <SID>Python_jump(motion) range
    let cnt = v:count1
    let save = @/    " save last search pattern
    mark '
    while cnt > 0
	silent! exe a:motion
	let cnt = cnt - 1
    endwhile
    call histdel('/', -1)
    let @/ = save    " restore last search pattern
endfun

last-patternを一旦退避させてますね。えらいですね。
この優しさがphpにも欲しかった。