82

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

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

93

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.

  • Cool! Works fast and simple! – DenisKolodin Nov 4 '16 at 9:59
  • 2
    how about providing an example? – Petr May 15 '17 at 11:30
23

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).

  • 6
    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 6 '15 at 11:41
  • Could somebody explain what does letter a in if a:expandtab mean? – john c. j. Feb 2 at 23:36
  • Also, it seems the conditions are reversed (a joke :D?). If you have set expandtab in your .vimrc and then autocmd BufWritePre * :call Retab(&expandtab) - indentation will be changed to tabs, instead of spaces. – john c. j. Feb 2 at 23:50
11

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.

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:

  • 1
    I would at least do %s/^\t\+/ g so that only the tabs used in indentation are converted. Also, gg=G could be catastrophic with languages like python. – Shahbaz Mar 4 '17 at 15:49
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.

  • 2
    What is expand? – muru Feb 1 '16 at 12:47
  • Expand is an inbuilt terminal command to copy tab character to spaces. Please find a detailed discription in the following link. computerhope.com/unix/uexpand.htm – Ankit Shah Feb 1 '16 at 13:48
  • 2
    If these are external commands, you should mention that. – muru Feb 1 '16 at 13:49
  • Further description added. Please review – Ankit Shah Feb 1 '16 at 13:52
  • 5
    You could also just do :%!expand -t 4 inside of vim. Better yet, you could also use the current shift width option: :exe '%!expand -t ' . &shiftwidth – EvergreenTree Feb 1 '16 at 21:29
1

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

  • 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 9 '18 at 5:56
  • Thanks @ideasman42; I've added to the answer to be more complete. – Samuel Li May 9 '18 at 21:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.