VIM: GotoSub

Ovid on 2008-07-16T14:50:45

I'm sure there's something I can do with ctags or something like that, but I actually like writing vim functions[1]. So my new function is to position the cursor on any subroutine name or method call, hit ",gs" and automatically either jump to that sub or be presented with a list of packages with that sub definition.

function! GotoSub(subname)
    let files  = []

    " find paths to modules with that sub
    let paths = split(system("ack --perl -l 'sub\\s+".a:subname."' lib t/lib"), "\n")

    if empty(paths)
        echomsg("Subroutine '".a:subname."' not found")
    else
        let file = PickFromList('file', paths)
        execute "edit +1 " . file

        " jump to where that sub is defined
        execute "/sub\\s\\+"  . a:subname . "\\>"
    endif
endfunction

Of course, you might wonder what that "pick from list" is. I keep needing to either let a user select from a list of choices (usually file names) or just return the one damned choice if there is only one.

function! PickFromList( name, list, ... )
    let forcelist = a:0 && a:1 ? 1 : 0

    if 1 == len(a:list) && !forcelist
        let choice = 0
    else
        let lines = [ 'Choose a '. a:name . ':' ]
            \ + map(range(1, len(a:list)), 'v:val .": ". a:list[v:val - 1]')
        let choice  = inputlist(lines)
        if choice > 0 && choice <= len(a:list)
            let choice = choice - 1
        else
            let choice = choice - 1
        endif
    end

    return a:list[choice]
endfunction

And the actual mapping is simple:

noremap  ,gs  :call GotoSub(expand(''))

And now I navigate through my code so much faster.

1. Though I spent a fair amount of time this morning writing bash to autogenerate database diffs and the transition was a bit confusing.