Skip to main content

Nvim: Improved Automatic Indentation

I am still surprised by hidden gems in nvim. The Zig plugin improves indentation support for the language. Learning from the included script revealed nvims indentation options. You might want to adjust those to your own taste.

Indentation Options

Firstly, make sure to set cindent as the following options depend on it. Then, refine cinoptions according your desires regarding indentation. My current options are the following.

setlocal cinoptions=(0,w1,j1,J1,l1,Ls

Let us go over them.

(0

In open parenthesis, align the next line with the open parenthesis. You can refine the exact position with the next option. The default is doubled indentation, which always needs adjustment according to my coding style.

w1

Adjusts (0 to point the cursor one position after (. This is exactly the right place to start typing my next parameter.

l1

Align next line with case label instead of the statement after it. This is useful if you start case blocks on the same line.

Ls

Place jump labels aligned with current code block.

j1

Use correct indentation for anonymous classes in Java.

J1

Use correct indentation for JavaScript object declarations.

This is my current configuration for quick reference. You might want to go over all options in :help cinoptions-values. Additionally, you might want to check whether they are triggered in the right places. Consult :help cinkeys accordingly.

Indentation Function

Predefined options are not the end of the road. Infinite possibilities can be defined via a custom function. If you are using a programming language not quite aligned with C syntax, this equips you with the right tool to adjust your indentation.

I demonstrate the usage on the example provided by the Zig plugin. Let us assume you want alignment of next line to => on the previous line.

function GetZigIndent(lnum)
  let curretLineNum = a:lnum
  let currentLine   = getline(a:lnum)
  let prevLineNum   = prevnonblank(a:lnum-1)
  let prevLine      = getline(prevLineNum)

  " switch (1): {
  "   1 => true,
  "       ~
  "       ^---- indents to here
  if prevLine =~ '.*=>.*,$' && currentLine !~ '.*}$'
     return indent(prevLineNum)
  endif

  return cindent(a:lnum)
endfunction

The function expects the current line number. We determine the previous non-empty line using prevnonblank(). Corresponding contents are available through getline().

For the previous line, we use =~ to check the presence of => and , being last. For the current line, we use !~ to check the absence of last }. If those conditions are met, we return the indentation of previous line. That is, we align the current line to the previous. The default consists of cindent().

Finally, we put our function into use setting indentexpr.

setlocal indentexpr=GetZigIndent(v:lnum)

You might find matchstrpos() handy for your own implementation. It takes a string and a regex to match. It then returns a list consisting of the match and both, the starting and ending position of it. Take a look at :help matchstrpos for the function’s usage. The returned positions can be used to adjust the line’s indentation.

Have fun with those improvements to your nvim configuration and write code more fluently.