ophinity :: papers :: java and vim

[ intro | quickfix | intra | inter | tips ]

Intro

The fixed-width text inside of boxes are lines from a script, .vimrc or the build.xml for Ant. The other fixed width text is meant to be characters typed into Vim while running Vim.

These examples were done on a RedHat Linux 8.0 machine with JDK 1.4.1, Ant 1.5.1 and Vim 6.1.

QuickFix

Instead of lugging punch-cards to the computer lab at 2am to check our code we now have the luxury of immediately building our code and verifying its correctness. The cycle of editing, compiling and editing again still is something of a bottle-neck when it comes to producing code quickly. That's where Vims Quickfix feature comes in. Quickfix can speed up the time it takes you to attempt a compile, read the compiler output, hunt down the offending file, find the line that is giving you trouble, fix it and move on to the next problem. In this section we'll talk about how to get QuickFix up and running.

QuickFix setup is easy. You just need to have a few things in place. We're assuming that you are using Ant to do your builds and you are using javac for the compiler.

We'll add a couple of auto-commands to our .vimrc file that only need to apply if we are working with Java files. The first line tells Ant what you want to do when you type :mak. The :mak command traditionally launches the make command, wich looks for a Makefile in the current directory. What we are doing is invoking Ant and pointing it to our build.xml file that is at the root of our project hierarchy.

The second line tells Vim how error lines look that come out of Ant and javac. Vim parses these lines and gives you information from the compiler as well as figuring out what lines and even column the offending code is on.

autocmd BufRead *.java set makeprg=ant\ -f\ /home/demian/code/jim/build.xml autocmd BufRead *.java set efm=%A\ %#[javac]\ %f:%l:\ %m,%-Z\ %#[javac]\ %p^,%-C%.%#

Using QuickFix

Okay with that in place, a working build.xml file and some code that you need to compile, you can start using QuickFix. Like anything in Vim there are lots of interesting options you can find about if you do a :help quickfix in Vim, but for our purposes here there are a handful of things that can get us going quickly.

To actually kick off the build you need to have Vim open, most likely you are editing one of the files that are to be built. Type in :mak and let Vim start the build. You will see normal output from an Ant build come across the screen along with your compile messages.

When the the compilation is done press enter. If there were any errors, you'll see the first one, press enter again and it will take you to where that error is at. So you fix that error, you can kick off the compile again by typing :mak or you can go to the next error that javac found. To go to the next error type :cn

Here is a table of the commands that let you navigate errors inside QuickFix. The c mnemonic on all of the commands is probably a leftover from the original use of the command; to navigate through C compiler error messages.

QuickFix Navigation

':' command what it does
cn go to the next error
cp go to the previous error
cfirst go to the first error
clast go to the last error
clist list all of the errors

Intra-File Navigation

Navigating around in files is one of the many reasons Vim has become a favorite editor for many developers. I will talk about some of the a-typical ways that vim makes it easy for you to get around a given file.

Folding

I consider folding a navigation aid because quite simply it makes it easier for you to get around. Imagine opening up a file and initially all you see is method declarations, any of wich you can open up when you want to edit them. This is what folding does for you.

There are a lot of options for folding but the simplest way to use it is to have it cue off of your indentation. Here are some typical settings for indentation, I won't explain them because there really aren't reasons to have to modify them on a regular basis.

set foldmethod=indent
set foldlevel=0
set foldnestmax=2
set fillchars=stl:_,stlnc:-,vert:\|,fold:\ ,diff:-

Admittedly the folding feature can, on occasion, seem to get in the way of working with a file. Because of that I have added a mapping to my .vimrc to toggle folding.

map F :let &fen = !&fen

Inter-File Navigation

The power of 'gf'

One useful tool for getting around to different files is the gf capability. This stands for go to file. When this is configured right, you will be able to put your cursor anywhere on the name of a class or an instance of a class and then press [esc]gf or [esc]gc. After that, you will see the new file in a split buffer window.

Like most things in Vim, the gf feature is primarily meant to work with the C/ C++ world. With some slight caressing we can get it working for us in the Java space.

First you need to set your path (see :help path). This is pretty much the root directory of your source tree. For our project this is /home/demian/code/jim/source So in Vim you would add a row like the following, one for every source directory that contains files you would like to jump to.

set path+=., set path+=/home/demian/code/jim/src/**,

To get Vim to understand Java import statements instead of C/ C++ include statements we need to add the following two lines to our .vimrc as well. The first line just says that the word 'import' is used, the second line replaces all occurrances of '.' with '/'.

autocmd BufRead *.java set include=^#\s*import
autocmd BufRead *.java set includeexpr=substitute(v:fname,'\\.','/','g')

Normally Vim will destroy your current buffer and take you to the new file. This isn't what I want 99% of the time. Usually I just want to see what's available in another class or something, so you can add the following mapping to your .vimrc. What this does is spawn a split window for the new buffer, so you can do this multiple times and still have your original file ready to work with.

map gf <C-W>f

Okay, so now we can put the cursor on a class name, Vim interprets the import statements and looks at the path you've defined and figures out what file to open. All we have to do is add another simple mapping and Vim will let you put the cursor on a class instance to get the same effect.

map gc gdb<C-W>f

This uses the gd command wich stands for go to definition. This goes to the definition of the instance does a back, so now you're on the class name. After that we just do the same gf to open up the file. Sweet huh?

Since you have added the path, Vim will also let you use the sfind command. You don't even need the class named in your code, now all you have to do is type in the class name and vim will find it in your path. Like this :sfind ClassName, and Vim will open the file in a new buffer for you.

Using Tag-Files

ctags is a great program used by a few editors, including Vim to index source code for artifacts that may be of interest, like class names and function names. ctags works fine with Java source. The index can then be querried by vim to figure out where something is located, be it a function or class.

There are a number of ways to create the tags file. I put it as a target within my Ant build script.

<target name="tags">
<exec executable="ctags">
<arg line="--recurse=yes"/>
<arg line="--links=yes"/>
<arg line="--java-types=cimp"/>
<arg line="-f .tags ."/>
<exec>
<target>

It's not enough just to have the tags file sitting around. You have to tell Vim where to find it. I added the following line in my .vimrc to point Vim to the tag file.

set tag=/home/demian/code/jim/.tags

When this is in place you can type ant tags and your tag file will be created. Now that Vim knows where it's at you can now actually use the tags. Go to the src/jim/main/Hello.java file and put the cursor over a class name or function name. Now press [ctrl]-] (that is, control and ']' at the same time). This should jump you to the file containing that artifact. To go back to the original file you press [ctrl]-t. Unfortunately that's not incredibly intuitive.

Okay that was file navigation with the help of tag files. Now we're going to talk about code-completion because it builds on the stuff that we just set up.

To tell Vim how you want to do code completion you just set a few things in your .vimrc.

set complete=i,],.,b,w,t,k,.
set dictionary=~/.vimKeywords

The complete line tells vim where you want to get the code-completion help from. See :help complete for more information. The dictionary file is a place where you can put common strings for code completion, this isn't necessary but might be helpful (see :help dictionary).

Now to actually do the code-completion while you're editing text you start typing and then press [ctrl]-n to go to the next match and [ctrl]-p to go to the previous. To me at least, this is not very intuitive either. So include this handy function in your .vimrc file.


inoremap =InsertTabWrapper ("forward")
inoremap =InsertTabWrapper ("backward")
function! InsertTabWrapper(direction)
  let col = col('.') - 1
  if !col || getline('.')[col - 1] !~ '\k'
    return "\"
  elseif "backward" == a:direction
    return "\"
  else
    return "\"
  endif
endfunction

What that function does is make it so that your [tab] button behaves just like it does for other editors that do code completion. Now you can go to the Hello.java file, start typing the name of a function or variable and then press the [tab] button and Vim will complete it for you.

General tips

Creating your package statement

Package statements can be a good opportunity to give yourself a compile error that takes 5 minutes to figure out (for me anyways)

To make sure this isn't an issue, i just paste the current path into the buffer and then turn that into my package statement. Go to where you are going to be putting your package statement and type.

[esc]:r!pwd[enter]

So what this does is just read in the pwd command from the shell. All this is assuming that you're source tree actually matches your package structure.

Don't forget about abbreviations

Abbreviations can be very useful if there is lengthy code that you have to type repeatedly. Just put lines like this in your .vimrc.

ab println System.out.println("");

You can even get cute and enter more Vim commands. For example the following abbreviation prints out the code, gets you into command mode, goes back twice and then puts you back into insert mode. That way the cursor is right in the middle of the quotes.

ab println System.out.println("");<esc>hhi

Indentation

Having Vim auto-indent while you are writing code is very useful but say you've pasted in some code, or moved code around in a brief re-factoring frenzy. The indenting feature can be a real time-saver in these situations.

The ugly code is probably within a set of brackets, move your cursor to one of the brackets and, in escape mode, press =% this tells Vim to indent all the lines from this bracket to the other bracket.