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.