222

I am getting 'trailing whitespace' errors trying to commit some files in Git.

I want to remove these trailing whitespace characters automatically right before I save Python files.

Can you configure Vim to do this? If so, how?

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Paul D. Eden
  • 19,939
  • 18
  • 59
  • 63

14 Answers14

224

I found the answer here.

Adding the following to my .vimrc file did the trick:

autocmd BufWritePre *.py :%s/\s\+$//e

The e flag at the end means that the command doesn't issue an error message if the search pattern fails. See :h :s_flags for more.

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Paul D. Eden
  • 19,939
  • 18
  • 59
  • 63
  • 1
    Interesting! Trailing white space is a battle at work. I loathe it, others don't understand why. We use as much vi as vim (I use vim; they don't because they'd have to install it). I have a program I call stb to Strip Trailing Blanks and I use that as a filter; works in vi too. This is better. – Jonathan Leffler Jan 15 '09 at 14:06
  • 22
    This changes cursor position on each save. Is is possible to avoid it? – stepancheg Oct 24 '09 at 15:59
  • OK, I've found, answer is below. – stepancheg Oct 24 '09 at 16:11
  • 6
    Since this is the de facto answer for this question maybe it should be updated to maintain cursor position. – Edu Felipe May 04 '11 at 14:26
  • 1
    see too keepjumps :h keepjumps – SergioAraujo Aug 31 '11 at 22:03
  • You could upvote the better answer until it become the best first one presented. – Sylvain Defresne Dec 08 '11 at 15:32
  • This do changes the cursor position on each save, however, a simple `gi` will put you back on the previous cursor position. –  May 09 '13 at 21:15
  • Using `:%!sed -r 's/\s+$//'` as the command seems to be another way (besides `keepjumps`) to prevent moving the cursor. See `:h filter`. – Victor Zamanian May 29 '13 at 12:54
  • 1
    `gi` jumps to the last position where text was inserted (not always the cursor position before the `:%s` command) and `:%!sed -r 's/\s+$//'` jumps to the beginning of the file. I don't get how `keepjumps` is supposed to be used, `:keepjumps :%s/\s+$//e` doesn't seem to do anything different. I'm using `:%s/\s\+$//e|normal ''` and it seems to work as I expect (goes to the cursor position before using the `:%s` command). – humodz Jul 19 '14 at 23:44
  • 1
    Correction for my previous comment: `:%s/\s\+$//e|normal ''` will jump to the wrong position if no trailing whitespaces were found. – humodz Jul 20 '14 at 00:49
  • 3
    This will also delete trailing spaces in multi-line strings, which may not be desired in some cases. But I guess there is no easy way to avoid this? – luator Aug 11 '15 at 08:58
  • 4
    Maybe you could explicit that the `e` at the end means, if we did not find the pattern, vi does not consider the substitute command as failed – LLenain Dec 01 '16 at 12:46
183

Compilation of above plus saving cursor position:

function! <SID>StripTrailingWhitespaces()
  if !&binary && &filetype != 'diff'
    let l:save = winsaveview()
    keeppatterns %s/\s\+$//e
    call winrestview(l:save)
  endif
endfun

autocmd FileType c,cpp,java,php,ruby,python autocmd BufWritePre <buffer> :call <SID>StripTrailingWhitespaces()

If you want to apply this on save to any file, leave out the second autocmd and use a wildcard *:

autocmd BufWritePre,FileWritePre,FileAppendPre,FilterWritePre *
  \ :call <SID>StripTrailingWhitespaces()
Hans Ginzel
  • 8,192
  • 3
  • 24
  • 22
stepancheg
  • 4,262
  • 2
  • 33
  • 38
  • 9
    You could better your function by also saving the last search and restoring it. let _s=@/ let @/=_s – xApple Jun 21 '11 at 14:58
  • For other noobs like me this is how you limit the files the function is called on: – Jason May 17 '12 at 22:28
  • Pure awesome. I use vim but am too lazy to look stuff up like this usually. This gave me a way to automate applying my arsenal of regexes on save. I'm stuck using Ruby for now, so I love not having to cut & paste my hash-rocket-killing regex.:wa – Keith Layne Jun 10 '13 at 18:30
  • 4
    I removed the ``autocmd FileType c,cpp,java,php,ruby,python `` part to make it apply to all files. – swt83 Aug 29 '13 at 18:50
  • I can't figure out why col(".") always returns 1 for me. – Eric Johnson Nov 01 '13 at 14:43
  • I had to add *. before each file extension. – Michael Stalker Dec 03 '13 at 15:18
  • 10
    @xApple: Inside functions, saving and restoring the last search is not necessary - leaving the function context will take care of that. – Tobias Feb 25 '14 at 13:16
  • 3
    @swt83 you need to also replace `` with `*` if you want it to work on all files – cadlac May 19 '14 at 01:32
  • Is there any good reason to only limit this to certain file types? – 425nesp Jan 20 '15 at 07:21
  • 4
    If you prefix the substitute command w/ `keepp`, it will not alter your search history, i.e. `keepp %s/\s\+$//e` – jeberle Jul 23 '16 at 04:55
  • 1
    Add an `undojoin` to stop this from polluting your undo history: `autocmd BufWritePre * undojoin | :call StripTrailingWhitespaces()` – tom-james-watson Apr 27 '20 at 10:23
  • Add `\m` to the pattern to be independent of the *magic* option. Save position better with `let l:save = winsaveview()` and `call winrestview(l:save)`. Add `if !&binary && &filetype != 'diff'` to ignore on binary and diff files (E.g. patch file to remove white spaces). – Hans Ginzel Jan 06 '21 at 10:00
70

I also usually have a :

match Todo /\s\+$/

in my .vimrc file, so that end of line whitespace are hilighted.

Todo being a syntax hilighting group-name that is used for hilighting keywords like TODO, FIXME or XXX. It has an annoyingly ugly yellowish background color, and I find it's the best to hilight things you don't want in your code :-)

l0b0
  • 55,365
  • 30
  • 138
  • 223
mat
  • 12,943
  • 5
  • 39
  • 44
  • 7
    Or you can set list and set listchars+=trail:. – Oli Dec 11 '08 at 07:17
  • Excellent - it's the perfect middle ground between automatically removing trailing whitespace (even when I may not be aware of it, or when it's someone else's code that I'm just happening to work in the same file with), to not doing anything about it. Thanks. – Daniel Hershcovich Jun 22 '11 at 07:13
  • 2
    unfortunately, my favorite color scheme zenburn doens't highlight – Peter Long Jul 22 '11 at 08:14
  • @PeterLong, doesn't work in railscasts theme either. Check it with `:hi Todo`. So I perused `:hi ` and `:help hi`. I considered `Cursor` and `Error`, but I think I'll try `match VisualNOS /\s\+$/ `. I might combine this with some of the `autocmd`s from other answers here. – Brady Trainor Jul 26 '14 at 22:16
56

I both highlight existing trailing whitespace and also strip trailing whitespace.

I configure my editor (vim) to show white space at the end, e.g.

enter image description here

with this at the bottom of my .vimrc:

highlight ExtraWhitespace ctermbg=red guibg=red
match ExtraWhitespace /\s\+$/
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
autocmd BufWinLeave * call clearmatches()

and I 'auto-strip' it from files when saving them, in my case *.rb for ruby files, again in my ~/.vimrc

function! TrimWhiteSpace()
    %s/\s\+$//e
endfunction
autocmd BufWritePre     *.rb :call TrimWhiteSpace()
l0b0
  • 55,365
  • 30
  • 138
  • 223
Michael Durrant
  • 93,410
  • 97
  • 333
  • 497
13

Here's a way to filter by more than one FileType.

autocmd FileType c,cpp,python,ruby,java autocmd BufWritePre <buffer> :%s/\s\+$//e
l0b0
  • 55,365
  • 30
  • 138
  • 223
syvex
  • 7,518
  • 9
  • 43
  • 47
10

I saw this solution in a comment at VIM Wikia - Remove unwanted spaces

I really liked it. Adds a . on the unwanted white spaces.

enter image description here

Put this in your .vimrc

" Removes trailing spaces
function TrimWhiteSpace()
  %s/\s*$//
  ''
endfunction

set list listchars=trail:.,extends:>
autocmd FileWritePre * call TrimWhiteSpace()
autocmd FileAppendPre * call TrimWhiteSpace()
autocmd FilterWritePre * call TrimWhiteSpace()
autocmd BufWritePre * call TrimWhiteSpace()
Diego Juliao
  • 414
  • 6
  • 16
8

Copied and pasted from http://blog.kamil.dworakowski.name/2009/09/unobtrusive-highlighting-of-trailing.html (the link no longer works, but the bit you need is below)

"This has the advantage of not highlighting each space you type at the end of the line, only when you open a file or leave insert mode. Very neat."

highlight ExtraWhitespace ctermbg=red guibg=red
au ColorScheme * highlight ExtraWhitespace guibg=red
au BufEnter * match ExtraWhitespace /\s\+$/
au InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
au InsertLeave * match ExtraWhiteSpace /\s\+$/
lazysoundsystem
  • 2,039
  • 23
  • 23
5

This is how I'm doing it. I can't remember where I stole it from tbh.

autocmd BufWritePre * :call <SID>StripWhite()
fun! <SID>StripWhite()
    %s/[ \t]\+$//ge
    %s!^\( \+\)\t!\=StrRepeat("\t", 1 + strlen(submatch(1)) / 8)!ge
endfun
l0b0
  • 55,365
  • 30
  • 138
  • 223
gregf
  • 1,091
  • 1
  • 11
  • 20
  • 2
    Hum, that's pretty dangerous to do it on "*" if you eventually open up binary files, they may end up in a pretty bad shape. – mat Dec 18 '08 at 20:45
  • Yeah probably not the smartest, then again I don't use vim for a hex editor either. That won't execute unless you save. – gregf Apr 01 '09 at 20:43
  • I think this beats the alternative of listing every single file type that you might work on, no? I work on rb, php, cs, html, sass, css, js, coffee, xml, xslt, pl, etc, etc, etc... Is there a happy medium? – Derek Prior Jun 18 '11 at 01:27
  • 4
    Unless you're writing to binary files in vim this probably won't ever be a issue. – gregf Jun 20 '11 at 21:47
  • It appears in the first `%s` the global (g) flag is as useless as a space at EOL :-) – Jens Feb 06 '13 at 15:41
3

A solution which simply strips trailing whitespace from the file is not acceptable in all circumstances. It will work in a project which has had this policy from the start, and so there are no such whitespace that you did not just add yourself in your upcoming commit.

Suppose you wish merely not to add new instances of trailing whitespace, without affecting existing whitespace in lines that you didn't edit, in order to keep your commit free of changes which are irrelevant to your work.

In that case, with git, you can can use a script like this:

#!/bin/sh

set -e # bail on errors

git stash save commit-cleanup
git stash show -p | sed '/^\+/s/ *$//' | git apply
git stash drop

That is to say, we stash the changes, and then filter all the + lines in the diff to remove their trailing whitespace as we re-apply the change to the working directory. If this command pipe is successful, we drop the stash.

Kaz
  • 55,781
  • 9
  • 100
  • 149
2

For people who want to run it for specific file types (FileTypes are not always reliable):

autocmd BufWritePre *.c,*.cpp,*.cc,*.h,*.hpp,*.py,*.m,*.mm :%s/\s\+$//e

Or with vim7:

autocmd BufWritePre *.{c,cpp,cc,h,hpp,py,m,mm} :%s/\s\+$//e
Mr_Pouet
  • 4,061
  • 8
  • 36
  • 47
1

The other approaches here somehow didn't work for me in MacVim when used in the .vimrc file. So here's one that does and highlights trailing spaces:

set encoding=utf-8
set listchars=trail:·
set list
kenorb
  • 155,785
  • 88
  • 678
  • 743
mb21
  • 34,845
  • 8
  • 116
  • 142
  • Executing `set listchars=trail:·` saying: `E474: Invalid argument: listchars=trail:·`. Can you validate your example? – kenorb Feb 16 '15 at 14:16
  • 1
    @kenorb see http://stackoverflow.com/questions/18321538/vim-error-e474-invalid-argument-listchars-tab-trail – mb21 Feb 16 '15 at 14:35
1

If you trim whitespace, you should only do it on files that are already clean. "When in Rome...". This is good etiquette when working on codebases where spurious changes are unwelcome.

This function detects trailing whitespace and turns on trimming only if it was already clean.

The credit for this idea goes to a gem of a comment here: https://github.com/atom/whitespace/issues/10 (longest bug ticket comment stream ever)

autocmd BufNewFile,BufRead *.test call KarlDetectWhitespace()

fun! KarlDetectWhitespace()
python << endpython
import vim
nr_unclean = 0
for line in vim.current.buffer:
    if line.rstrip() != line:
        nr_unclean += 1

print "Unclean Lines: %d" % nr_unclean
print "Name: %s" % vim.current.buffer.name
cmd = "autocmd BufWritePre <buffer> call KarlStripTrailingWhitespace()"
if nr_unclean == 0:
    print "Enabling Whitespace Trimming on Save"
    vim.command(cmd)
else:
    print "Whitespace Trimming Disabled"
endpython
endfun

fun! KarlStripTrailingWhitespace()
    let l = line(".")
    let c = col(".")
    %s/\s\+$//e
    call cursor(l, c)
endfun
KP99
  • 378
  • 1
  • 9
  • You could also keep search register `let _s=@/` and restore it at the end ` let @/=_s` .In this case we are using black hole register – SergioAraujo Nov 23 '16 at 13:02
1
autocmd BufWritePre *.py execute 'norm m`' | %s/\s\+$//e | norm g``

This will keep the cursor in the same position as it was just before saving

frippe
  • 1,329
  • 1
  • 8
  • 15
0

autocmd BufWritePre * :%s/\s\+$//<CR>:let @/=''<CR>

aemonge
  • 2,289
  • 1
  • 24
  • 26