145

It's pretty common when programming or opening text files to encounter files with trailing whitespace at the end of a line. vim has a way to show this by setting the trail option in the listchars option and then turning list on.

However, what's the easiest way to eliminate that trailing whitespace globally across the whole of a file (ideally without a plugin)?

73

Use a keybinding to strip all trailing whitespace

Since some pages that I edit actually need trailing whitespaces (e.g. markdown) and others don't, I set up a keybinding to F5 so that it's trivial to do without being automatic. To do so, add the code below (from vim.wikia) or some variation of it to your .vimrc:

"Remove all trailing whitespace by pressing F5
nnoremap <F5> :let _s=@/<Bar>:%s/\s\+$//e<Bar>:let @/=_s<Bar><CR>
  • nnoremap <F5> does a nonrecursive mapping to the key F5 in normal mode
  • :let _s=@/ stores the last search term (from the macro @/) in the variable _s
  • <Bar> Functions as a pipe symbol | to separate commands, however | would end a command in this context, so <Bar> must be used instead.
  • :%s/\s\+$//e searches for trailing whitespace and deletes it everywhere in the buffer (see CarpetSmoker's answer for a detailed breakdown of this expression)
  • let @/=_s restores your last search term to the macro @/, so that it will be available the next time you hit n.
  • <CR> ends the mapping

... or be more selective

If you have cases in which you don't want to strip all of the trailing whitespace, you can use a pattern to be more selective. For example, the following code shows how I strip trailing whitespace only if it comes after a semicolon (here it's tied to F8).

nnoremap <F8> :let _s=@/<Bar>:%s/;\s\+$/;/e<Bar>:let @/=_s<Bar><CR>

This is useful if, like me, you have some files with markdown-like heredocs interspersed among semicolon-terminated programming statements.

  • 6
    Try :keeppatterns to prevent overriding @/. And also take a look at :keepjumps. – Bohr Mar 7 '15 at 6:14
  • @Bohr What version of vim are you using? I tried :help keeppattern and didn't get anything. – Christopher Bottoms Mar 9 '15 at 22:01
  • @ChristopherBottoms At least version 7.4.155. – Bohr Mar 10 '15 at 4:51
  • @Bohr. Thanks! Come to find out I was still using 7.4.0. I installed the latest version and it's available. – Christopher Bottoms Mar 10 '15 at 14:08
  • 2
    You can get the same effect by wrapping this command in a function, since then the last search term is automatically restored :-) This way you don't have to muck about with :nohl either, if if you were highlighting something, it will keep highlighting it (see my updated answer). – Martin Tournoij Jun 25 '15 at 16:39
180

The "simplest" way is to just use :substitute:

:%s/\s\+$//e
  • :%s to run :substitute over the range %, which is the entire buffer.
  • \s t match all whitespace characters.
  • \+ to repeat them 1 or more times.
  • $ to anchor at the end of the line.
  • The e flag to not give an error if there is no match (i.e. the file is already without trailing whitespace).

However, this is probably not the "best" way as it causes two side-effects:

  1. it moves the cursor to the last match;
  2. it adds the command to the history and search history;
  3. it resets the last search term.

You can fix both items by turning this into a function:

fun! TrimWhitespace()
    let l:save = winsaveview()
    keeppatterns %s/\s\+$//e
    call winrestview(l:save)
endfun

And then use it like:

:call TrimWhitespace()
  1. The winsaveview() will save the current "view", which includes the cursor position, folds, jumps, etc. The winrestview() at the end will restore this from the saved variable.
  2. The :keeppatterns prevents the \s\+$ pattern from being added to the search history.
  3. The last-used search term is automatically restored after leaving a function, so we don't have to do anything else for this.

Since this is somewhat annoying to type :call all the time, you can define a command:

command! TrimWhitespace call TrimWhitespace()

Which can be be used without the :call:

:TrimWitespace

And you can of course bind it to a key:

:noremap <Leader>w :call TrimWhitespace()<CR>

Some people like to automatically do this before they write a file to disk, like so:

autocmd BufWritePre * :call TrimWhitespace()

I don't like it, as some formats require trailing whitespace (such as Markdown), and on some other occasions you even want trailing whitespace in your code (such as formatting an email, and using the --<Space> marker to indicate the start of a signature).


Shameless plug mode: a while ago I wrote a little Python script to clean up whitespace for an entire project at once.

  • 1
    If you don't want to create a function to move to the previous position you could just press ​`​ twice after the replace is finished. This opens the possibility to create a oneliner like this: %s/\s\+$//e | exe "normal ``" – Neaţu Ovidiu Gabriel Mar 8 '16 at 10:00
  • 1
    @NeaţuOvidiuGabriel, of course then the double backtick will not work the same after the oneliner is executed. ;) – Wildcard Jan 10 '17 at 2:04
  • Similar: stackoverflow.com/a/1618401. But I like Martin's code more. – john c. j. Jan 31 at 11:25
12

To delete all trailing whitespace (at the end of each line), you can use the command:

:%s/ \+$//

To include tabs, use \s instead of space.


From the command-line:

$ ex +'%s/\s\+$//e' -cwq file.c

All the files in the current directory (recursively use **/*.*):

$ ex +'bufdo!%s/\s\+$//e' -cxa *.*

Python way:

:py import vim
:pydo vim.current.buffer[linenr - 1] = vim.current.buffer[linenr - 1].strip()

or:

:py import vim
:py for i, l in enumerate(vim.current.buffer): vim.current.buffer[i] = l.rstrip()

Use lstrip() for left strip (trailing), rstrip() for right strip (leading) or strip() to remove from both ends.


Here is useful function which removes superfluous white space from the end of a line which you can add to your .vimrc:

" Removes superfluous white space from the end of a line
function! RemoveWhiteSpace()
   :%s/\s*$//g
    :'^
    "`.
endfunction

There is also DeleteTrailingWhitespace plugin for that.


Highlighting white spaces

To double-check if all trailing spaces are gone, use:

  1. Type / $ to find them. If there are some, vim would highlight them for you.

  2. Use colours to highlight them:

    :highlight ws ctermbg=red guibg=red
    :match ws /\s\+$/
    
  3. Use visible characters (source):

    :set encoding=utf-8
    :set listchars=trail:·
    :set list
    

See also: Highlight unwanted spaces

To highlight trailing whitespace by default, you may configure your .vimrc as follow:

highlight ws ctermbg=red guibg=red
match ws /\s\+$/
autocmd BufWinEnter * match ws / \+$/

Removing white spaces by default

If you would like to make sure that all trailing whitespace in a file are removed automatically on save, you may add the following command into your .vimrc:

autocmd BufWritePre *.c,*.php :%s/ \+$//ge

which is not recommended, as it'll strip trailing whitespace from every file a user saves (even where whitespace can be desired).


See also:

5

Exapnding on Christopher Bottoms's answer a bit: Jonathan Palardy wrote a good article on this. In it, he writes a functions, Preserve(command), that preserves the state of the editor (mainly cursor position and last search pattern) while running an arbitrary command:

function! Preserve(command)
  " Preparation: save window state
  let l:saved_winview = winsaveview()
  " Run the command:
  execute a:command
  " Clean up: restore previous window position
  call winrestview(l:saved_winview)
endfunction

This has the advantage being multipurpose, for example you can use it to replace all trailing whitespace (as Jonathan does) by mapping this to :

nnoremap <F5> :call Preserve("%s/\\s\\+$//e")<CR>

You can also use it for a visual mode mapping to just remove the trailing spaces on visually selected lines:

xnoremap <F5> :call Preserve("'<,'>s/\\s\\+$//e")<CR>

And you can use it for other calls, like formatting the whole document using = while retaining your place (well use a different key this time so as not to conflict):

nnoremap <F6> :call Preserve("normal gg=G")<CR>

All and all, I've found the Preserve(command) function to be a nice tool to have.

  • 2
    The last used search term should be automatically preserved when you leave a function; so mucking about with @/ should not be required (in this case, anyway). – Martin Tournoij Jun 25 '15 at 16:43
  • 3
    winsaveview() and winrestview() are far superior. – dash-tom-bang Mar 5 '16 at 1:01
  • Quite right! Updated based on your feedback. – Alex Dec 14 '17 at 22:13
0

Another version of the StripTrailingSpaces function:

if !exists('*StripTrailingWhitespace')
    function! StripTrailingWhitespace() range
        if !&binary && &filetype != 'diff'
            call Preserve(":" . a:firstline . "," . a:lastline . "s/\\s\\+$//e")
        endif
    endfunction
endif

" http://technotales.wordpress.com/2010/03/31/preserve-a-vim-function-that-keeps-your-state/
if !exists('*Preserve')
    function! Preserve(command)
        try
            let l:win_view = winsaveview()
             "silent! keepjumps keeppatterns execute a:command
            silent! execute 'keeppatterns keepjumps ' . a:command
        finally
            call winrestview(l:win_view)
        endtry
    endfunction
endif

Actually there is a bug in this function (this one):

The position is not being kept because of the "range" option. It it is removed, and it works very well. However, I am sharing the code to receive some help.

As you can see, it also uses the Preserve function seen above, but in a slightly different fashion.

The difference here is that I can select a range of lines or a paragraph with vip and then the range :'<,'> will automatically appear at the command prompt.

The idea came from a Bez Hermoso post.

  • Re "I am sharing the code to receive some help": Are you asking a question? – Peter Mortensen Nov 5 at 20:14
  • Sorry for taking so long giving you feedback. The needed "Preserve" function was finally added. Now my answer makes some sense. It is an attempt to make getting rid of trailing spaces easier. – SergioAraujo Nov 10 at 11:11

protected by Martin Tournoij Jan 9 '17 at 19:19

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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