120

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

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

1
  • If you have installed vim-faq, you can get an offline answer there: :h vim-faq and search /tab characters. The hard to memorize tag is :h faq-14.16.
    – Hotschke
    Jul 23 '19 at 7:55
128

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.

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

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

2
  • 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 6 '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 at 19:25
24

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:

2
  • 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
  • 1
    :%s/\t/ /g is the best solution and the easiest.
    – Amruth A
    Apr 9 '20 at 7:01
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.

5
  • 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
  • 6
    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 Feb 1 '16 at 21:29
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

2
  • 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.