135

Is it possible to convert tabs to spaces, while maintaining text alignment?

Simply replacing only works usefully when there are no leading characters.

ideasman42
  • 3,733
  • 3
  • 27
  • 34

5 Answers5

145

You can use the :retab command. From :help :retab

Replace all sequences of white-space containing a <Tab> with new strings of white-space using the new tabstop value given. If you do not specify a new tabstop size or it is zero, Vim uses the current value of 'tabstop'. [...] With 'expandtab' on, Vim replaces all tabs with the appropriate number of spaces.

Note that the command accepts a range, so you can make a visual selection and then just :retab the selected lines.

Martin Tournoij
  • 62,054
  • 25
  • 192
  • 271
guillem
  • 2,361
  • 1
  • 16
  • 11
30

You can use :retab, as stated, however, this will change all tabs to spaces, not only tabs at the start of the line

So this (where is a tab character):

if :; do
⇥echo "⇥hello"
end

gets changed to (where is a space character):

if :; do
␣␣echo "␣␣hello"
end

This can produce unexpected side-effects in some scenarios, and it's even more of an issue when changing spaces to tabs!

So, I wrote a little function to change only tabs/spaces at the start of the line:

" :retab changes *everything*, not just start of lines
fun! Retab(expandtab)
    let l:spaces = repeat(' ', &tabstop)

    " Replace tabs with spaces
    if a:expandtab
        silent! execute '%substitute#^\%(' . l:spaces . '\)\+#\=repeat("\t", len(submatch(0)) / &tabstop)#e'
    " Replace spaces with tabs
    else
        silent! execute '%substitute#^\%(\t\)\+#\=repeat("' . l:spaces . '", len(submatch(0)))#e'
    endif
endfun

With this version, you have to manually specify expandtab in the function call (ie. :call Retab(1) to change tabs to spaces), but you could also modify it to take the current value of &expandtab (as it already does with &tabstop) just like :retab does. (I happen to prefer to specify it manually).

Krabby127
  • 3
  • 4
Martin Tournoij
  • 62,054
  • 25
  • 192
  • 271
  • 7
    There is also an answer on SO which covers retabbing at start of line only. It is worth linking because of the clarity of that explanation. – jalanb Feb 06 '15 at 11:41
  • @JSV if you wish to suggest an edit to the author's code, I recommend leaving a comment about possible corrections or posting your own answer, especially when the new code is significantly different. – D. Ben Knoble Sep 15 '21 at 19:25
30

Vim provides :retab! command which will replace all sequences of <Tab> with new strings of white-space using the new tabstop (e.g. :set tabstop=2) value given, but all tabs inside of strings can be modified (e.g. in a C program, you should use \t to avoid this)!

So alternatively you can change all tabs into spaces using the following command:

:%s/\t/  /g

or as suggested by @Shahbaz:

:%s/^\t\+/ /g

So only the tabs used in indentation are converted.

Note: there is a subtle difference between both commands. The first one will replace each Tab character by two spaces, the second one replace any number of leading Tab characters in a line by a single space.

Explanation:

  • % represents the entire buffer/file (:help :%)
  • s stands for substitute (:help sub-replace-special)
  • \t, or ^I stands for tab
  • - use as many spaces as you need per one tab
  • g - stands for global, and it'll convert multiple occurences of tabs in the same line

Then to correct indentation of the entire file, you may try: gg=G. Check: Re-indenting badly indented code for more details.

To use spaces by default instead of tabs, you need to add the following settings into your .vimrc file:

set tabstop=2     " (ts) width (in spaces) that a <tab> is displayed as
set expandtab     " (et) expand tabs to spaces (use :retab to redo entire file)
set shiftwidth=2  " (sw) width (in spaces) used in each step of autoindent (aswell as << and >>)

Alternative solution is to use tidy


Related:

Christian Brabandt
  • 25,820
  • 1
  • 52
  • 77
kenorb
  • 18,433
  • 18
  • 72
  • 134
3

For completeness, = could also be used to fix indentations, after you have specified that tabs are replaced with spaces. In normal mode, you can do so by typing :set expandtab. Then = could be used in two ways:

  • In Visual mode, a single = would fix indentations of selected code blocks.
  • In normal mode, gg=G would fix the entire file, where gg moves the cursor to the beginning of the file, then = is applied, and G moves the cursor to the end of the file.

Reference: link

Samuel Li
  • 131
  • 3
  • This assumes vim can recompute the current indentation correctly (may not be the case) - also for unstructured files, or code written in a different editor with slightly different indentation rules. – ideasman42 May 09 '18 at 05:56
  • Thanks @ideasman42; I've added to the answer to be more complete. – Samuel Li May 09 '18 at 21:07
3

Try using:

expand -t 4 input_filename output_filename

expand is a command-line tool to convert tabs to spaces, which you can run from a shell or with :!expand.

It's in POSIX so it should be available on most systems. unexpand will do the reverse, by the way.

Martin Tournoij
  • 62,054
  • 25
  • 192
  • 271
Ankit Shah
  • 155
  • 5