← back to latest

Fix a line's indent without leaving insert mode

INSERT<C-t>Fix a line's indent without leaving insert mode

anatomy

<C-t>
In insert mode, add one shiftwidth of indent at the start of the current line. The cursor can be anywhere on the line. It stays on the same character, and you stay in insert mode.

In the buffer

With :set shiftwidth=4 expandtab, you have typed the third line and the cursor is still in insert mode after the closing parenthesis:

def load(path):
    with open(path) as f:
    data = json.load(f)
                       ^

After typing <C-t>:

def load(path):
    with open(path) as f:
        data = json.load(f)
                           ^

The line moves right by four spaces, and the cursor moves with it, so it is still after the ). You are still in insert mode, so you can keep typing, and the next character lands at the end of the line as if nothing had happened.

When you would reach for it

You are typing a line and notice it belongs one level deeper, or one level shallower, than where autoindent put it. The usual fix is <Esc>, >>, then A to get back to the end of the line. That is three steps, and it breaks your typing. <C-t> and its twin <C-d> reindent the line from where you are, wherever the cursor is, and leave you in insert mode. This is the fastest way to fix indentation in YAML, Python, and nested lists while you are still writing them.

Gotchas

  • The step size is shiftwidth, not tabstop. Under vim -u NONE, shiftwidth is 8 and expandtab is off, so <C-t> on a line with four spaces of indent produces a single tab character. Set shiftwidth and expandtab for the filetype, or <C-t> puts in indent that does not match the rest of the file.
  • The result rounds to a multiple of shiftwidth. With shiftwidth=4, a line indented by three spaces goes to four after <C-t>, not seven. <C-d> rounds the other way: six spaces with shiftwidth=4 go to four. This is the fix you usually want, but it can surprise you on a line that was deliberately misaligned.
  • It reindents the whole line, not the text after the cursor. In the middle of data = json.load(f), <C-t> does not insert spaces at the cursor. It moves the entire line, and the cursor keeps its place in the text. If you wanted whitespace at the cursor, use <Tab>.
  • In Neovim and some setups, a completion plugin or a custom mapping may already use <C-t> or <C-d> in insert mode. If nothing happens, run :verbose imap <C-t> to see what owns it.

Variants

<C-d>

Removes one shiftwidth of indent. With shiftwidth=2, - name: a (six spaces) becomes - name: a (four spaces), and you stay in insert mode.

0<C-d>

Type 0 and then <C-d> to remove all indent from the line. Vim deletes the 0 you typed as well, so x becomes x.

^<C-d>

Removes all indent from the current line only. With autoindent on, the next line you open gets the old indent back. Use this for a C label or a preprocessor line like #ifdef DEBUG that has to start in column 1 in the middle of an indented block.

lineage

Ctrl-t and Ctrl-d in input mode come from Bill Joy's vi, where they existed to work with autoindent: Ctrl-t pushed the indent out one shiftwidth and Ctrl-d pulled it back. The 0 Ctrl-d and ^ Ctrl-d forms are vi too. Classic vi tied these keys to the autoindent at the start of the line, and vi clones still disagree about what they do once you have typed text. Vim made them reindent the whole line from anywhere, keeping vi's rule that the result is rounded to a multiple of shiftwidth.

-- INSERT --vim-hacks.com027-027-reindent-a-line-without-leaving-insert-mode.html
utf-81,1All