Tab settings in Vim

Ari Sweedler
5 min readAug 8, 2018

--

Summary:

There are 4 settings that you may be concerned with. Each setting’s value answers a question

  • tabstop answers the question: how many columns of whitespace is a \t char worth? Think of a set of vertical lines running down the length of your paper.
  • shiftwidth answers the question: how many columns of whitespace a “level of indentation” is worth?
  • softtabstop answers the question: how many columns of whitespace is a tab keypress or a backspace keypress worth?
  • Setting expandtab means that you never wanna see a \t again in your file — rather, tabs keypresses will be expanded into spaces

999 times outta 1000, I’ll want all integer options set to be the same. In abbreviated notation, that means we'll run a command like so: :set ts=2 sw=2 sts=2 et.

Foreword:

  • If you don’t know how settings work in Vim, read this really fast (link)
  • You might want to make whitespace visible before reading this. We can use the list & listchars settings for that. Run :set listchars=space:_,tab:>~ list. You will now see spaces as colored _ characters, and tabs as a single > followed by as many ~s as needed.

Alright! Let’s make something simple complicated. There are a few fundamental ways to get whitespace into Vim, and they all serve different purposes. Thus, they use refer to different settings in order to do their job right. Here are the ways to get whitespace into your file: the spacebar key, the tab key, the shift-left-right commands(< or >), and the formatting commands (= or gq ).

In order to determine how to place whitespace into your file upon receiving one of those commands, Vim refers to the values of the following settings.

  1. tabstop — How many columns wide is a tab character worth? Does a \t take up 4 columns? 2 columns? 8 columns? Changing this value will change how tabs are displayed. Must be greater than 0, as tabs having no width is never desired.
  2. shiftwidth — Referred to for “levels of indentation”, where a level of indentation is shiftwidth columns of whitespace. That is, the shift-left-right commands, the formatting commands, and the behavior of vim with cindent or autoindent set is determined by this setting. A shift command says “give me one more (or one less) level of indentation”. Formatting commands are more complicated (:help gq if you’re curious, they basically parse your file and determine how many layers of indentation should be there). So if vim decides that a line needs 3 levels of indentation then it’ll place 3*shiftwidth columns of whitespace.
  3. expandtab — Unlike the other settings, this one is a Boolean, and quite straightforward. It answers the question: should vim place spaces or tab upon receiving a whitespace command or a tab keypress.
  4. softtabstop — Referred to for the tab key and backspace key. How much whitespace should be inserted when the tab key is pressed? And how much whitespace should be removed when the backspace key is pressed?

By default, softtabstop is disabled, meaning that hitting tab will add whitespace columns until vim gets to a tabstop (either 1 \t character or tabstop spaces, based on if expandtab is set). And hitting backspace will remove 1 character. This default behavior is weird when expandtab is set, the lack of parallelism feels weird: pressing tab will add tabstop characters to your file, but backspace will only remove 1. People normally want backspace to remove a tab’s worth of whitespace when they have expandtab set. So if you’re enabling expandtab then you should enable softtabstop.

Alright! Not too bad. Let’s just review really quickly:

  • tabstop is effectively how many columns of whitespace a \t is worth.
  • shiftwidth is how many columns of whitespace a “level of indentation” is worth.
  • Setting expandtab means that you never wanna see a \t again in your file
  • softtabstop is how many columns of whitespace a tab keypress or a backspace keypress is worth.

Well, that made it sound simple. But it gets kinda nuanced:

  • By default (softtabstop=0, noexpandtab), a tab keypress will give you a \t character. And a backspace keypress will remove 1 character.
  • If you set expandtab, then a tab keypress will give you tabstop spaces. A backspace keypress will remove 1 character.
  • If you set softtabstop, then a tab keypress will give you softabstop spaces. A backspace keypress will remove softabstop columns of whitespace (or 1 character, if you’re in the middle of a block of text and stuff).

If you understand the abstractions that the designers of vim made, then everything is simple! Here’s how I understand it: enabling softtabstop says that backspace and tab should change of columns of whitespace instead of characters. And orthogonally related, shiftwidth is all about levels of indentation.

Alright! Let’s look at some examples! Just for fun.

Example 1: We have tabstop=5, and softtabstop=3. We are in insert mode on an empty line.

Hitting tab will give you 3 columns of whitespace. Can we apply expandtab? Nope.

Hitting tab again will change your total to 6 columns of whitespace. There are now enough columns of whitespace to fit a \t.

  • If noexpandtab, as is default, then vim will fill up the 6 columns of whitespace with 1 \t character and 1 space.
  • If expandtab is set, then vim will fill up the 6 columns of whitespace with 6 spaces.

This example was meant to show you that a \t character is equal to tabstop columns of whitespace, and that pressing the tab key is worth softtabstop columns of whitespace.

Example 2: We have tabstop=8, softtabstop=0, shiftwidth=5, and noexpandtab. We are in insert mode on an empty line.

softtabstop is disabled. Hitting tab will give you tabstop (8) columns of whitespace. Because noexpandtab is set as well, Vim inserts 1 \t character to fill these 8 columns.

Hitting tab again will change your total to 16 columns of whitespace. The file sees \t\t.

You run :set expandtab tabstop=4. The two tabs on screen each become worth 4 columns of whitespace, but they don’t turn to spaces yet. You have 8 columns of whitespace. The file’s contents haven’t changed, but what you see on the screen has.

You hit >>, adding a level of indentation (shiftwidth columns of whitespace) to your file. Well, 8+5 = 13 columns of whitespace. Vim expands the tabs, and you will get 13 spaces.

Had you set noexpandtab before running the indent command, then you’d still have 13 columns of whitespace. But instead of Vim placing 13 spaces, it would place 3 \t characters and 1 space.

As you can see, you can do some kinda weird stuff with these settings. To not get confused, there’s only a few things you have to memorize. tabstop is the maximum columns of whitespace a \t character can take up, shiftwidth is for “levels of indentation”, and softtabstop is for keypresses.

Generally, you’ll want to have all these values be the same, just for consistency’s sake. That way, you can edit and hit the tab key or >> or backspace and affect the same number of columns of whitespace.

And that’s it. Woo! ⚪️👾🎉

Hey Winnie, here’s some free relationship advice!!! Jot down `set softtabstop=8 expandtab` in your vimrc file

End note 1: Check out retab, it’s a super easy way to change tabs to spaces in a document. Also check out what CTRL-t does in insert mode.

End note 2: Knowing these settings works super will with the command filetype plugin on. That command says: upon opening a file of type xyz, source the file ~/.vim/ftplugin/xyz.vim.

This is awfully convenient if you wish to run setlocal tabstop=2 shiftwidth=2 expandtab every time you open a *.c file, and setlocal tabstop=8 shiftwidth=8 noexpandtab every time you open a Makefile. You can check out what’s in my ~/.vim folder here. Ask me anything! I’d love to talk to someone that cares about this LOL.

--

--

Responses (4)