Vim Tips Wiki
Tip 1592 Printable Monobook Previous Next

created 2008 · complexity basic · author Metacosm · version 7.0

This tip explains how :retab converts tabs to spaces, or spaces to tabs, and provides a "super retab" command to convert only the whitespace used for indentation in programs.

Standard retab[]

You can set the 'expandtab' (abbreviated to 'et') option so each tab that you type is converted to an equivalent number of spaces. And you can use the :retab command to convert all existing tabs to spaces. You can do both in one command:

:set et|retab

You can also convert spaces to tabs:

:set noet|retab!

Both of the above examples should be used with caution. They convert all sequences, even those that might be in a "quoted string like this".

This tip shows how to convert only the indents at the left margin. Any spaces or tabs after the first non-white character are not affected.

Super retab[]

Use the following command to define a new SuperRetab command. You could enter this in Vim, or put it in your vimrc:

:command! -nargs=1 -range SuperRetab <line1>,<line2>s/\v%(^ *)@<= {<args>}/\t/g

For example, you may have a code snippet which uses two-space indents, and you want to entab the indents (convert each leading group of two spaces to a tab). To do this, visually select the code (press V then j), then enter:

:'<,'>SuperRetab 2

The above command would change:

  for {

to the following ("|-------" represents a tab):

|-------for {

The command :SuperRetab 5 would give the same result from the following selected text:

     for {


An alternative super retab procedure is to use the following two commands:

:command! -range=% -nargs=0 Tab2Space execute '<line1>,<line2>s#^\t\+#\=repeat(" ", len(submatch(0))*' . &ts . ')'
:command! -range=% -nargs=0 Space2Tab execute '<line1>,<line2>s#^\( \{'.&ts.'\}\)\+#\=repeat("\t", len(submatch(0))/' . &ts . ')'

The above defines a Tab2Space and a Space2Tab command that convert leading whitespace (spaces and tabs that are not at the beginning of a line are not affected). These commands use the current 'tabstop' (abbreviated as 'ts') option.


" Convert all leading spaces to tabs (default range is whole file):
" Convert lines 11 to 15 only (inclusive):
" Convert last visually-selected lines:
" Same, converting leading tabs to spaces:


A more elaborate solution is to use the following script which provides these features:

  • The commands allow an argument to specify the column width; if none is given, the 'tabstop' setting is used.
  • Redundant spaces in an indent are removed (in the above mapping, converting tabs to spaces will not change lines where there is a space before a tab in the indent).
  • The search history is not changed (pressing n will do the same search it would have done before the conversion was performed).
  • The cursor position is restored, although the column will be slightly wrong owing to the different number of characters in the indent.

These commands are provided:

Space2Tab Convert spaces to tabs, only in indents.
Tab2Space Convert tabs to spaces, only in indents.
RetabIndent Execute Space2Tab (if 'expandtab' is set), or Tab2Space (otherwise).

Each command accepts an argument that specifies the number of spaces in a tab column. By default, the 'tabstop' setting is used.

" Return indent (all whitespace at start of a line), converted from
" tabs to spaces if what = 1, or from spaces to tabs otherwise.
" When converting to tabs, result has no redundant spaces.
function! Indenting(indent, what, cols)
  let spccol = repeat(' ', a:cols)
  let result = substitute(a:indent, spccol, '\t', 'g')
  let result = substitute(result, ' \+\ze\t', '', 'g')
  if a:what == 1
    let result = substitute(result, '\t', spccol, 'g')
  return result

" Convert whitespace used for indenting (before first non-whitespace).
" what = 0 (convert spaces to tabs), or 1 (convert tabs to spaces).
" cols = string with number of columns per tab, or empty to use 'tabstop'.
" The cursor position is restored, but the cursor will be in a different
" column when the number of characters in the indent of the line is changed.
function! IndentConvert(line1, line2, what, cols)
  let savepos = getpos('.')
  let cols = empty(a:cols) ? &tabstop : a:cols
  execute a:line1 . ',' . a:line2 . 's/^\s\+/\=Indenting(submatch(0), a:what, cols)/e'
  call histdel('search', -1)
  call setpos('.', savepos)
command! -nargs=? -range=% Space2Tab call IndentConvert(<line1>,<line2>,0,<q-args>)
command! -nargs=? -range=% Tab2Space call IndentConvert(<line1>,<line2>,1,<q-args>)
command! -nargs=? -range=% RetabIndent call IndentConvert(<line1>,<line2>,&et,<q-args>)

See also[]