Friday, March 20, 2009

Out of the box Autocompletion

Thanks to Chris Sutter for contributing the following tip:

(The following applies to versions of vim higher than 7.0)

If you are editing a file in vim which ends with .php, .html, .css, .js, .sql, .rb, or .py, you can type (while in insert mode) ctrl-x followed by ctrl-o and vim's "omnifunc" feature combines with its built-in autocomplete feature to show autocomplete options specific to the corresponding language/markup. You can subsequently press ctrl-n and ctrl-p to browse through this list. In short, vim has autocomplete functionality for all common web development contexts (and probably some others) out of the box. But it's not just autocomplete.

If you are editing php (or, i imagine, ruby/python but i didn't try) and the text on which you are autocompleting is the beginning of a valid (builtin) function name, a small window will open at the top of your vim screen showing the method signature with argument names! No more jumping over to php.net to see if it's (needle, haystack) or (haystack, needle)...

Example:
Edit a file called foo.php, enter insert mode, type

<?php
str_r

Now press ctrl-x ctrl-o and vim shows a box below the cursor containing the following, with the first entry highlighted:

str_repeat( f
str_replace( f
str_rot13( f

The "f"s indicate that each entry is a function. If you had entered $_ and pressed c-x c-o, the results would have been "$_COOKIE", "$_ENV", and so on, labeled with "v" for variable)

The window opened above (since the selection is a function) shows:

str_repeat(string input, int multiplier | string

telling you the full function name, the argument types and names and finally the return type.

Admittedly, the c-x c-o and c-n/c-p stuff is awkward. I imagine you could remap to tab/shift-tab which is more common for autocompletion browsing in unix environments. The SuperTab plugin for vim does this for normal file-wise autocompletion (otherwise done in insert mode with c-n and c-p). It could probably be hacked to incorporate omnifunc stuff, too. If I see anything like that out there, I'll send something about it.

Please let me know if there's anything you think vim can't do and I'll see to ensuring you that, in fact, it can and it's easier than you think!

16 comments:

Peter Krantz said...

Oh yeah? Does it tell you the weather like Eclipse does? :-)

w0m said...

weather
{
declare -a WEATHERARRAY
zip="zip"
WEATHERARRAY=( `lynx -dump "http://www.google.com/search?hl=en&lr=&client=firefox-a&rls=org.mozilla%3Aen-US%3Aofficial&q=weather+${zip}&btnG=Search" | grep -A 5 -m 1 "Weather for" | sed "s/ - \[.*iGoogle/ - /"`)
echo ${WEATHERARRAY[@]}
}

Emmanuel said...

Can vim do mru buffer switching on ctrl-tab ? I look for this for a long time.

brycethornton said...

This is an awesome tip. Thanks!

redbeard0531 said...

If you add these lines to your vimrc, SuperTab will do smarter completion. If you have a '/' it will do filename completion, otherwise it does ^X^O (omni-completion).

let g:SuperTabDefaultCompletionType = "context"
let g:SuperTabContextDefaultCompletionType = "<c-x><c-o>"

take a look at the Global Variables section of the supertab script for more config options

brycethornton said...

Is it possible to jump to the function definition from a piece of code that uses the function? This is for PHP specifically, but for any language would be great?

redbeard0531 said...

@brycethornton:

Take a look at :help tags. It works with all languages that exuberant-ctags supports which does include PHP. The only down side is that it isn't as smart as the omni complete code so if there are multiple tags with the same name (think toSting in JAVA) it won't use context to pick the right one. If this becomes an issue, I suggest mapping ^] to g^] so that it will present a list when there is more than one match.

wtanksley said...

Challenge taken.

Can vim be configured to operate using a quasimode rather than as fully modal? For example, it'd be in insert mode normally, then switch to command mode so long as I'm holding down capslock (or some other key), and as soon as I release, it's back in insert mode.

riffraff said...

wtanksley, take a look at Cream for vim. Great post anyway :)

the DtTvB said...

For Mac users, put this in ~/.vimrc, this enables omnicomplete.

filetype plugin on

Also on Ubuntu, you can install vim-python to enable autocompletion for Python.

wtanksley said...

Cream (and Easy Mode) give a non-modal interface (like most editors), not a quasimodal one. Unless there's something new in Cream in the last few days...

grey wolf said...

@Bryce: Ctrl+]. You may need to set up your tags file. :help Ctrl-]

freegnu said...

A couple of things.

The up and down arrows work like ctrl-n and ctrl-p on Mac OS X with the console vim when a omnicomplete list is on screen.

That weather script is a Bash function definition and changing "zip" to $1 passes in the first argument to weather on the command line. Like so: weather 10001

Anonymous said...

It works with .pl files too. I'd like to know how to turn it off for Perl though. Takes way too much time to scan the perl modules for the auto-complete possibilities.

Casey

freegnu said...

Try this command to change the auto completion to the simpler syntax highlighting hints as the auto completer.

:setlocal omnifunc=syntaxcomplete#Complete

If that is what you always want for Perl add this to your .vimrc after all your other auto commands.

autocmd Filetype perl setlocal omnifunc=syntaxcomplete#Complete

If you want syntax completion for all file types use * for the file type. If you want to fail over to syntax complettion for file types that don't have omni completion put this in your .vimrc after your other auto commands.

if has("autocmd") && exists("+omnifunc")
autocmd Filetype *
\ if &omnifunc == "" |
\ setlocal omnifunc=syntaxcomplete#Complete |
\ endif
endif

You can also do something a little more complex which just disable the perlPOD part of the perl filetype omni complete.

Try this command:

let g:omni_syntax_group_exclude_perl = 'perlPOD'

generically it's:

let g:omni_syntax_group_exclude_{filetype} = 'comma,separated,list'

And you can get the complete list of syntax groups while in a file with the filetype in question with this command:

:syntax list

Then just add that command to your .vimrc to make it permanent.

Check out this help topic for more info:

:h ft-syntax-omni

sac said...

To switch buffers with Ctrl-Tab you can do the following:

:map <C-Tab> :bnext<CR>

This will "round" the opened buffers in the current window.

Me myself have this set of mappings:

map <C-Tab> <C-W><C-W>
map <S-Right> :bnext<CR>
map <S-Left> :bprevious<CR>

with these mappings, if you use :split or :vsplit to span more than one "window" you can rotate focus between them using Ctrl-Tab. To switch the buffer on the current selected window then I use Shift-Left and Shift-right.

Hope this helps.