Vim で関数の呼び出し元のスクリプト番号を取得する

最近教えてもらった黒魔術を覚書。
と、いうかだいぶ改造したので原型があまり残っていない(基本的なロジックはそのままだけど。
簡単に説明すると g:SID() をつくりました。
とりあえず、こんな感じで使えます。

[使い方]

function! s:main()
    " 呼び出し元の関数名を返す
    echo g:call_funcname()
    " => <SNR>290_main
    
    " SID 付きスクリプト関数の prefix を返す
    echo g:SID_function_prefix()
    " => <SNR>290_

    " SID を返す
    echo g:SID()
    " => 281

    " スクリプト関数の参照を返す
    echo g:script_function("s:plus")(1, 2)
    " => 3
endfunction
call s:main()


これで s:SID() を作成する必要はなくなった…はず。

[注意]

上記のように g:SID() を使用するためには制限があり、スクリプトローカル関数から呼び出した場合しか使えません。
どういう事かというとグローバル空間やグローバル関数から呼び出した場合は使えない。

" グローバル空間では使えないや
echo g:SID()

function! g:global()
    " グローバル関数内では使えない
    echo g:SID()
    " => ''

    " こっちは動作する
    call s:main()
endfunction
call g:global()


あとはまだあんまりアグレッシブなテストを行なっていないのでまだ何か問題があるかも。

[ソース]

function! s:SCaller(...)
    try
        throw 'abc'
    catch /^abc$/
        if a:0
            let prefunc = a:1
            let result = matchstr(v:throwpoint, '^.*\.\.\zs.*\ze\.\.'.prefunc)
            if empty(result)
                let result = matchstr(v:throwpoint, '^function \zs.*\ze\.\.'.prefunc)
            endif
            return result
        else
            return matchstr(v:throwpoint, '^function \zs.\{-}\ze\.\.')
        endif
    endtry
endfunction


function! g:call_funcname()
    return s:SCaller("g:call_funcname")
endfunction


function! g:SID_function_prefix()
    return matchstr(s:SCaller("g:SID_function_prefix"), '^\zs<SNR>\d\+_\ze.*$')
endfunction


function! g:SID()
    return matchstr(s:SCaller("g:SID"), '^<SNR>\zs\d\+\ze.*$')
endfunction


function! g:script_function(name)
    let SID = matchstr(s:SCaller("g:script_function"), '^\zs<SNR>\d\+_\ze.*$')
    let funcname = substitute(a:name, "s:", SID, "g")
    return function(funcname)
endfunction