In vim quickfix feature consists of a dedicated buffer that holds the results, such as grep and make, and various commands to handle it. It’s a feature that is commonplace in IDEs, but in vim you can combine it with other features to make your editing operations much more efficient.

We have a :make and a :grep command, and they play nicely with the quickfix feature.

How does it work?

  1. Execute the external command with the specified argument.
  2. Parses the output of file names, line numbers, messages, etc.
  3. Makes a list that can be used for jumps

To open the quickfix window with :copen and close with :cclose. In the buffer we have a list and hit enter on them will jump to that file on that specific line.

When we call :make it uses the program specified in grepprg, and :grep uses makeprg. We can play with these variables to get things done. If the output format is not right, we can configure it with grepformat and errorformat.

Bu default it will print out to stdout and jumps to the first match, we sometimes it’s useful, but for me it’s mostly annoying, to disable this we can call :silent grep! where silent will suppresses the output and the ! will tell the command to do not jump to the first result.

What can we use in format

Vim has a very nice and detailed :help errorformat, but here is a shorter list:

  • %f: file name (finds a string)
  • %o: module name (finds a string)
  • %l: line number (finds a number)
  • %c: column number (finds a number representing character column of the error, byte index, a is 1 character column)
  • %n: error number (finds a number)
  • %m: error message (finds a string)
  • %r: matches the “rest” of a single-line file message %O/P/Q
  • %%: the single ‘%’ character
  • %s: search text (finds a string)
  • %E: start of a multi-line error message
  • %W: start of a multi-line warning message
  • %I: start of a multi-line informational message
  • %N: start of a multi-line note message
  • %A: start of a multi-line message (unspecified type)
  • %>: for next line start with current pattern again
  • %C: continuation of a multi-line message
  • %Z: end of a multi-line message

Multi-line can tricky, let’s see an example. We have output in this format:

Error 275
line 42
column 3
' ' expected after '--'

To match this, we can use:

set errorformat=%EError\ %n,%Cline\ %l,%Ccolumn\ %c,%Z%m

Why is it good?

If we have a nice quickfix list, we can jump between them with :cnext and :cprevious, so we can list all the errors, jump to the first one, fix it, call :next fix it, and so on.

Use The Silver Searcher or Ripgrep (grep)

By default it uses grep, but we can change with grepprg:

" Set grepformat
set grepformat=%f:%l:%m
" The Silver Searcher
set grepprg=ag\ --vimgrep\ $*
" Ripgrep
set grepprg=rg\ --vimgrep

Git grep (grep)

" Set grepformat
set grepformat=%f:%l:%m
" Use git grep
set grepprg=git\ --no-pager\ grep\ -n\ --no-color

Use golangci-lint (make)

set errorformat=%E%f:%l:%c:\ %m,%C%s,%C%s
set makeprg=golangci-lint\ run

Location list

The quickfix buffer is unique, we can have only one, sometimes it would be useful if we have multiple list and we can do that with location list.

This is a cool feature of quickfix, you open if once and all :make or :grep will update that buffer. With location list, we can have multiple ones open and when we call :make or :grep it will not update the buffer we already have.

Make our life easier with :compiler

Usually we have more than on tools and it would be painful to set makeprg and errorformat all the time. Fortunately we have a tool to do that and it’s call :compiler. We can define compiler sets in $VIM/compiler/.

For example we can define a compiler called golangci in ~/.config/nvim/compiler/golangci.vim (or ~/.vim/compiler/golangci.vim):

if exists("current_compiler")
let current_compiler = "golangci"

if exists(":CompilerSet") != 2
  command -nargs=* CompilerSet setlocal <args>

CompilerSet errorformat=%E%f:%l:%c:\ %m,%C%s,%C%s
CompilerSet makeprg=golangci-lint\ run

With this compiler we can switch to this specific compiler when we want to use it with :compiler golangci. It does nothing but updates the errorformat and makeprg variables. With :compiler! it will be global, otherwise it sets only for the buffer.