vim: add a 'use' statement without moving the cursor

You're writing Perl code in vim and have just typed a package name - maybe you want to create an object of this class:

some_statement;
my $o = Some::Class->new;
do_something_with($o);

You obviously need to write use Some::Class at the top. So you either move the cursor near the top and add the line, then jump to the previous line number, or maybe you split the window, move to the new viewport, make the change, then close that viewport.

There is a better way. Here is a vim function for adding a use statement to the top of your file. I prefer it to go below my last existing use statement, except for special cases like use strict, use constant etc. If there isn't any such regular use statement, I want the new use statement to appear below the ABSTRACT (a Dist::Zilla-related comment) or below the package statement. Failing that, I want the use statement to appear below any existing use statement. If there isn't one of those either, below the shebang line. If we don't even have that, just put it at the top of the file.

Your aesthetic requirements may vary, so change the function accordingly.

This is my first real Vim script function that I've written, so excuse the 'baby Vim'.

" perl: add 'use' statement
"
" make sure you have
"   setlocal iskeyword=48-57,_,A-Z,a-z,:
" so colons are recognized as part of a keyword

function! PerlAddUseStatement()
    let s:package = input('Package? ', expand('<cword>'))
    " skip if that use statement already exists
    if (search('^use\s\+'.s:package, 'bnw') == 0)
        " below the last use statement, except for some special cases
        let s:line = search('^use\s\+\(constant\|strict\|warnings\|parent\|base\|5\)\@!','bnw')
        " otherwise, below the ABSTRACT (see Dist::Zilla)
        if (s:line == 0)
            let s:line = search('^# ABSTRACT','bnw')
        endif
        " otherwise, below the package statement
        if (s:line == 0)
            let s:line = search('^package\s\+','bnw')
        endif
        " if there isn't a package statement either, put it below
        " the last use statement, no matter what it is
        if (s:line == 0)
            let s:line = search('^use\s\+','bnw')
        endif
        " if there are no package or use statements, it might be a
        " script; put it below the shebang line
        if (s:line == 0)
            let s:line = search('^#!','bnw')
        endif
        " if s:line still is 0, it just goes at the top
        call append(s:line, 'use ' . s:package . ';')
    endif
endfunction

map ,us :<C-u>call PerlAddUseStatement()<CR>

5 Comments

How about us Emacs users?

Something like this seems to work (pardon the lame formatting):

(defun my-perl-insert-use (name)
(interactive
(let ((x (thing-at-point 'symbol)))
(list (read-string (format "Module [%s]: " x) nil nil x))))
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^\\s *\\(use\\|package\\)" nil t))
(unless (= (point) (point-min))
(forward-line 1))
(insert (format "use %s;\n" name))))

This is awesome. It's going into my vim configuration right now. Thanks for sharing! :-)

The function itself can ensure we get a correct and otherwise uninterfering 'iskeyword':


function! PerlAddUseStatement()
    let l:save_iskeyword = &l:iskeyword
    setlocal iskeyword=48-57,_,A-Z,a-z,:
    let s:package = input('Package? ', expand('<cword>'))
    let &l:iskeyword = l:save_iskeyword
    ...

(You might also want to change the 's:' variables to 'l:' variables, scoping them a bit tighter.)


Thanks for the script!

Leave a comment

About Marcel Grünauer

user-pic perlservices.at is a B2B service provider for Perl in Vienna, Austria. I'm a professional software developer since 1989 and a Perl specialist since 1998. I also like Go (囲碁), Japanese and Korean culture and language.