1. Introduction

The Vi editor supports many features to compose and structure texts quickly and easily. One of them is indenting.

In this tutorial, we explain some indenting options and how to change the amount of indentation for each. First, we go over general indentation amounts. Next, we check the indent size control for operators and commands. Finally, we review the automatic indentation settings and how to configure the shift amounts for each.

For brevity, we’ll use vi (Vi) when referencing both the Vi and Vim editors. Also, we use the angle bracket notation to represent characters with names from the standard tables.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.

2. General Indent Amounts

In Vi’s Insert mode, we can indent by hand via the or characters.

Moreover, the tabstop (ts) variable dictates the number of <space**> characters equal to one . By default, one is eight (8) characters:

:set tabstop=2

Here, we set tabstop to 2.

Yet, we might not want to use at all. Then, we can set the expandtab option to replace any characters with one or more instances of :

:set expandtab

Now, Vi will insert the appropriate amount of characters for any .

3. Indent Amount for Operators and Commands

In addition to the options above, Vi provides shiftwidth (sw), which determines the indentation amount for operators, commands, and settings:

:set shiftwidth
[...]
  shiftwidth=8

For example, we can use the > right shift and < left shift operators to increase and decrease the indentation of one or more lines:

5>>

Yet, we can also use commands:

:>>5

Critically, the :5>> command indents line 5, while 5>> indents the current line and each of the following 4 lines by shiftwidth times. The command equivalent to the 5>> operation is :>>5.

Moreover, the minimal value of shiftwidth is 0, while shiftround decides whether Vi rounds indentation to a multiple of shiftwidth.

In addition, single shift operators require a motion:

5>w

Finally, the count of <* or *> determines how many times Vi indents with shiftwidth characters:

:>>>>>>

In this case, Vi indents only the current line six (6) times.

4. Indent Amount for Settings

Of course, the autoindent, smartindent, cindent, and indentexpr settings are standard when it comes to automating indentation.

However, the amount of indentation and how to set it for each differs.

4.1. autoindent

Normally, autoindent takes the indent amount of the previous line as a guide. If it was three spaces, we get three spaces in the beginning when going to the next line.

This is the simplest auto-indent case since it doesn’t need any other settings for the amount of indentation.

4.2. smartindent

While smartindent relies on autoindent and its behavior, it also increases the amount of indentation based on several rules:

  • following lines that end with an { opening brace
  • following lines that start with any keyword in cinwords, which we can also modify
  • before lines that start with a } closing brace

Moreover, smartindent also performs some indentation-specific actions:

  • mirrors the indentation of matched opening and closing braces, i.e., blocks of code
  • strip indentation from lines that start with an # octothorp, leaving the latter in the first column

When either cindent is set, or indentexpr isn’t empty, setting smartindent doesn’t have any effect.

4.3. cindent

In the case of cindent, typing any of the keywords in cinkeys in Insert mode triggers a reindenting of the current line, while cinoptions sets the way that indentation is performed.

For example, let’s consider the default value of cinoptions:

cinoptions=>s,e0,n0,f0,{0,}0,^0,L-1,:s,=s,l0,b0,gs,hs,N0,E0,ps,ts,is,+s,
  c3,C0,/0,(2s,us,U0,w0,W0,k0,m0,j0,J0,)20,*70,#0,P0

The cinoptions settings are comma-separated and consist of two parts: condition (one character) and indent amount (following characters). In detail, the latter can contain a negative or positive number (optionally with a decimal point) and the letter s for shiftwidth, in that order. At least one should be present.

Negative values at the beginning mean we reverse any previous indentation at most to the beginning of the line. On the other hand, shiftwidth is the indentation [s]tep. If shiftwidth=0, the value of tabstop replaces it.

Now, we can decode some of the default cinoptions settings above:

  • >s sets the amount for normal indent to one shiftwidth
  • f0 sets the indentation amount for non-nested opening braces that begin a line to 0
  • ts sets the additional indentation of a function return type to one shiftwidth

Essentially, cindent and cinoptions provide very fine control over when and how much indentation is inserted. Yet, if indentexpr isn’t empty, it takes precedence over any cindent and smartindent actions.

4.4. indentexpr

Lastly, the value of indentexpr, coupled with the contents of indentkeys, provides complete freedom in terms of the lines that get indented.

Following is the default value of indentkeys:

:set indentkeys
[...]
  indentkeys=0{,0},0),0],:,0#,!^F,o,O,e

Basically, vi evaluates indentexpr when triggered by one of several events:

  • create new line
  • use the = equals operator, which indents based on cindent unless indentexpr isn’t empty
  • type any indentkeys keywords in Insert mode

While the format of indentkeys is equivalent to that of cinkeys, indentexpr itself can be more complex. The string in indentexpr is an expression that should return the indent amount (in a number of characters) for the current line. Commonly, this means the expression is a function call without any arguments, which performs any necessary evaluations in the current context. Returning -1 means autoindent kicks in.

5. Summary

In this article, we explored different ways to indent in the Vi editor and how to set the indentation amount for each.

In conclusion, while Vi’s options can be comprehensive and understanding them can be daunting, they provide choice and flexibility when it comes to indentation control.