137

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
    Commented Jul 23, 2019 at 7:55

5 Answers 5

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.

3
  • Cool! Works fast and simple! Commented Nov 4, 2016 at 9:59
  • 3
    how about providing an example?
    – Petr
    Commented May 15, 2017 at 11:30
  • :ret is 2 fewer keystrokes. Does the same thing Commented Dec 1, 2021 at 2:11
31

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:

3
  • 2
    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
    Commented Mar 4, 2017 at 15:49
  • 1
    :%s/\t/ /g is the best solution and the easiest.
    – Amruth A
    Commented Apr 9, 2020 at 7:01
  • +1 for the .vimrc part Commented Jan 12 at 20:36
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).

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
    Commented Feb 6, 2015 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
    Commented Sep 15, 2021 at 19:25
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
    Commented Feb 1, 2016 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
    Commented Feb 1, 2016 at 13:48
  • 2
    If these are external commands, you should mention that.
    – muru
    Commented Feb 1, 2016 at 13:49
  • Further description added. Please review
    – Ankit Shah
    Commented Feb 1, 2016 at 13:52
  • 7
    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 Commented Feb 1, 2016 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
    Commented May 9, 2018 at 5:56
  • Thanks @ideasman42; I've added to the answer to be more complete.
    – Samuel Li
    Commented May 9, 2018 at 21:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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