vimtricks.wiki Concise Vim tricks, one at a time.

How do I use a Vimscript function to control how the gq operator formats text?

Answer

:set formatexpr=MyFormat()

Explanation

The formatexpr option lets you replace Vim's built-in line-breaking logic for the gq operator with your own Vimscript expression or function. This gives complete programmatic control over how text is reformatted — unlike formatprg, which delegates to an external program via a shell pipe, formatexpr runs inside Vim and has access to all of Vim's internal state including buffer variables, line content, and cursor position.

How it works

When you invoke gq{motion} (or gqq for the current line), Vim checks:

  1. If formatexpr is set, it calls that expression. The expression must format lines v:lnum through v:lnum + v:count - 1 in place and return 0 on success (non-zero falls back to internal formatting)
  2. If formatprg is set, it pipes the lines through the external program
  3. Otherwise, Vim applies its built-in algorithm respecting textwidth and formatoptions

Key variables available inside the expression:

  • v:lnum — first line to format
  • v:count — number of lines to format
  • v:char — character that triggered formatting (when called from formatoptions+=a)

Example

A minimal formatexpr that delegates to an external LSP formatter (Neovim):

set formatexpr=v:lua.vim.lsp.formatexpr()

Or a custom Vimscript function that right-justifies text to column 80:

function! RightJustify() abort
  for lnum in range(v:lnum, v:lnum + v:count - 1)
    let line = getline(lnum)
    call setline(lnum, printf('%80s', trim(line)))
  endfor
  return 0
endfunction
set formatexpr=RightJustify()

Tips

  • Set formatexpr per filetype using autocmd FileType to apply different formatters for different languages
  • formatexpr takes precedence over formatprg when both are set
  • In Neovim, the built-in LSP client sets formatexpr automatically when attaching to a language server that supports range formatting — this is how gq becomes a proper code formatter
  • Use :set formatexpr= (empty) to clear it and fall back to formatprg or Vim's internal formatter

Next

How do I make Vim display partial key sequences and selected character counts in real time?