My Favorite Vim Function, ever!

Ovid on 2008-06-06T08:11:17

This could use some work, but it's quickly become an indispensable navigation tool for me. Place your cursor on any package name and type ',gm' (GotoModule -- assumes your leader is a comma) and you will automatically jump to that module.

Features:

  • Uses @INC to find the same modules Perl would find.
  • If only one version is found, automatically jumps to it.
  • If more than one version is found, you will be presented with a list to pick from.
  • Cached searches for better performance.
  • Highlighted "Module '$module' not found" if the module is not found.
let g:perl_path_to = {}
function! GotoModule(module)
    let files  = []

    if !has_key(g:perl_path_to, a:module)
        let g:perl_path_to[a:module] = []
        let lib    = split(system("perl -le 'print join $/ => @INC'"), "\n")
        let module = substitute(a:module, '::', '/', 'g') . '.pm'

        for path in lib
            let path = path . '/' . module
            if filereadable(path)
                let g:perl_path_to[a:module] = g:perl_path_to[a:module] + [ path ]
            endif
        endfor
    endif

    let paths = g:perl_path_to[a:module]
    if empty(paths)
        echomsg("Module '".a:module."' not found")
    else
        let file = PickFromList('file', paths)
    endif
    execute "edit " . file
endfunction

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

There are a couple of bugs (such as still trying to edit a file if you cancel), but I'll work 'em out later. For now, you probably want the following added to your .vimrc to make those work:

" only works for Perl
au! FileType perl :noremap  gm  :call GotoModule(expand(''))

" make sure we pick up the colon as part of our keyword
autocmd FileType perl setlocal iskeyword+=:

" don't kill 'undo' in other buffers
set hidden

I now use this thing constantly to quickly and easily navigate to any module listed in my code, even core modules. It's really sped things up for me.


gf

Smylers on 2008-06-06T09:34:49

Sounds good, but could you clarify what this does over Vim's supplied gf command? That's normally "goto file", but for Perl files it munges colons and @INC to DTRT. (Also for Ctrl+W f, which opens a new window with the file.

Re:gf

Ovid on 2008-06-06T09:52:57

Because it always puts you to the first incarnation of that file and this makes it very problematic for me. Due to the large size of our project, we have many locally patched modules (28 and growing) and if there is more than one version of a given module, I need to see this. As is often the case with vim, its functions don't quite do what I need.