Vim Quickfix

Published on Thursday, January 29, 2015

By Brian Shourd

I'm going to talk today about vim's quickfix window, and how you can use it to make the whole "build, fix, build, test, fix, build, ..." cycle faster and easier.

The problem

If you're like me, a good chunk of your coding time is spent in a cycle that looks like:

  1. Make some edits
  2. Compile
  3. If step 2 failed, goto 1. Else, goto 4.
  4. Run automated tests
  5. Goto 1

Note: I'm purposely ignoring the software-development process that goes on around these things (like when/how you write tests, when/how you commit your work, the size of the units of work). I just want to focus on the logistics of actually doing these things in a painless way.

I used to run these processes in different windows. A vim window for editing, a terminal window for building, and a terminal for running tests. This has some issues:

  1. You now have to use the information displayed on one window to inform your edits in another. Best case, you can see both windows at once, but often this isn't true.
  2. Your compiler tells you where the errors are, but you have to manually go to that location.
  3. Changing windows is precious keystrokes (or worse, the dreaded mouse)!

The solution: automate all the things

Before I talk about what all of this means, let me give you the sneak peek in to what a basic quickfix workflow would look like

  1. Edit a file - :e MyClass.cs
  2. Select our compiler - :compiler xbuild
  3. Build the project - :Make
  4. Navigate to first build error - :cc

Those four steps will look something like this:

Quickfix window example

(Code shown is just a basic Queue implementation. There is a typo, where I tried to access this.Typo, instead of this.Value, which is what the error is about.)

The window at the bottom of the screen there - that's the quickfix window. When xbuild failed, it popped up with that convenient highlighted error message, and jumped me directly to the location in the file that needed fixing.

The quickfix window is capable of giving this kind of functionality with any command that outputs file locations (file name, line number, column number). You can ask vim to run the command, parse this information on your behalf, and display it to you in the quickfix window.

How do I do it?

The functionality of running a command and interpreting the output as a series of file locations is described in some very simple files, which vim calls compiler plugins.

(I find the name "compiler" to be a bit loaded and misleading - I guess they were named that because they often correspond to actual compilers, but as I said they can really correlate to any sort of command that outputs information of this type. Compilers, test runners, linting rules, searching tools - all of these are valid commands to be turned into vim compiler plugins.)

Many different compiler plugins are built-in. Typing :compiler into vim will print a list of all of them installed on your system. My system lists:

bcc, bdf, checkstyle, cs, cucumber, decada, dot, erlang, eruby, fortran_cv, fortran_elf90, fortran_F, fortran_g77, fortran_lf95, fpc, g95, gcc, gfortran, gnat, go, haml, hp_acc, icc, ifort, intel, irix5_c, irix5_cpp, javac, jikes, mcs, mipspro_c89, mipspro_cpp, mips_c, modelsim_vcom, msbuild, msvc, neato, ocaml, onsgmls, pbx, perl, php, pyunit, rake, rspec, rst, ruby, rubyunit, sass, se, splint, tcl, tex, tidy, xbuild, xmllint, xmlwf

Using one of these is simple. First, type

:compiler xbuild

to select the xbuild.vim compiler plugin for the current buffer (:compiler! xbuild will select it for all buffers, so you don't have to set the compiler again until you close vim). Then call

:make

to run the command specified by the compiler plugin in the current working directory. You can supply arguments to :make that will be passed on as well, e.g.

:make MySubLibrary/MySubLibrary.csproj

When the compile command finishes, your vim window will return. You can open the quickfix window with :copen. Use j and k to navigate lines, then Return to jump to the selected error. You can use :cn[ext], :cp[revious], and many other built-in commands to navigate through the list of errors. Read more about this with :help quickfix.

These commands are very useful, since vim will jump directly to the location of errors, even if those errors are in another file. This can save a lot of time navigating the filesystem and navigating within files, especially if the code you are working on is distributed across several areas.

Better builds and :Make

You might notice that by default, :make blocks your vim session. So you can't view or edit files until it returns. And when it returns, it doesn't automatically open the quickfix window for you.

That's totally lame.

Enter Tim Pope and his magnificent vim-dispatch plugin. This plugin introduces a few new commands, chief among them :Make, :Dispatch, and :Focus. :Make replaces :make, except that instead of blocking input, the build process runs elsewhere (in a new pane if you are using tmux, a new tab in iTerm, a background window, or just invisibly in a background process), then pops up the quickfix window when it is done (if there were any errors). It never steals focus, and allows you to build without interrupting your flow at all. Basically fantastic.

:Dispatch allows you to do the same with other tasks, without having to specify a compiler. This is great for interleaving a build and the running of certain tests. Suppose that I have a project that I'm building with the xbuild compiler and testing with a custom nunit compiler I wrote (the nunit compiler runs a script called runNunitTests). Then I can use :compiler! xbuild so that :Make always runs xbuild for me. When I want to run a test, I use instead

:Dispatch runNunitTests NameOfA.TestFixture.ToRun

It behaves exactly like :Make (opening a process in the background and piping the results to the quickfix window), but will automatically select the appropriate compiler based on the command supplied, and won't change the setting of :compiler. So I can interleave :Dispatch and :Make.

If I will be running the same test fixture over and over, I may want to :Focus on it. I start with

:Focus runNunitTests NameOfA.TestFixture.ToRun

And now I can just run :Dispatch with no arguments to dispatch on my focus.

For even more fun, bind :Make and :Dispatch to function keys. Build and/or test at the push of a button without interruption!

Summary

With just a few commands, you can really make a strong workflow in vim. Not just for editing, but for the whole coding process. But what if...

  • You've got an obscure compiler that isn't listed?
  • You've got a test suite that you want to run (like the nunit runner I talked about above)?
  • Your build process is actually a complicated multi-step process (build -> lint -> minify -> test) that you want to all be done and parsed at once?

Then you will just have to write your own vim compiler plugin. This is a lot less terrifying than it sounds. In fact, in my next post I'll show you how to do just that - create a new compiler plugin for running nunit tests.