Vim script で stacktrace 書いてみた

[関連]

github.com

これを Vim script だけでどこまで出来るかやってみた

[コード]

let s:V = vital#of("vital")
let s:S = s:V.import("Palette.Scriptnames")

function! s:call_stack()
    try
        throw 'abc'
    catch /^abc$/
        return split(matchstr(v:throwpoint, 'function \zs.*\ze,.*'), '\.\.')[ : -2]
    endtry
endfunction


function! s:parse_script_function(text)
    let pat = '^<SNR>\(\d\+\)_\(\w\+\)\[\(\d\+\)\]'
    let result = matchlist(a:text, pat)
    if empty(result)
        return {}
    endif

    let path = s:S.find("SID == " . result[1]).file
    return {
\       "kind" : "script",
\       "path" : path,
\       "SID"  : result[1],
\       "name" : result[2],
\       "lnum" : result[3],
\   }
endfunction


function! s:parse_lambda_function(text)
    let pat = '\<lambda>\(\d\+\)\[\(\d\+\)\]'
    let result = matchlist(a:text, pat)
    if empty(result)
        return {}
    endif

    return {
\       "kind" : "lambda",
\       "ID"   : result[1],
\       "name" : "",
\       "lnum" : result[2],
\   }
endfunction


function! s:parse_global_function(text)
    let pat = '\(\w\+\)[\(\d\+\)\]'
    let result = matchlist(a:text, pat)
    if empty(result)
        return {}
    endif

    return {
\       "kind" : "global",
\       "name"   : result[1],
\       "lnum" : result[2],
\   }
endfunction


function! s:parse(text)
    let result = s:parse_script_function(a:text)
    if !empty(result)
        return result
    endif

    let result = s:parse_lambda_function(a:text)
    if !empty(result)
        return result
    endif

    let result = s:parse_global_function(a:text)
    if !empty(result)
        return result
    endif

    return {}
endfunction


function! s:stacktrace()
    let stack = reverse(s:call_stack()[:-2])
    return map(stack, "s:parse(v:val)")
endfunction


function! Global()
    echo PP(s:stacktrace())
endfunction

function! s:sub2() abort
    return Global()
endfunction

function! s:sub1() abort
    let F = {-> s:sub2() }
    call F()
endfunction

function! s:main() abort
    call s:sub1()
endfunction
call s:main()

[出力結果]

[
    {'kind': 'global', 'lnum': '1', 'name': 'Global'},
    {
        'SID': '693',
        'kind': 'script',
        'lnum': '1',
        'name': 'sub2',
        'path':
            '/Users/rbtnn/a.vim'
    },
    {'ID': '66', 'kind': 'lambda', 'lnum': '1', 'name': ''},
    {
        'SID': '693',
        'kind': 'script',
        'lnum': '2',
        'name': 'sub1',
        'path':
            '/Users/rbtnn/a.vim'
    },
    {
        'SID': '693',
        'kind': 'script',
        'lnum': '1',
        'name': 'main',
        'path':
            '/Users/rbtnn/a.vim'
    }
]

グローバル関数と lambda 関数はファイル名が取得できなかった…。