ghostkadost
ghostkadost

Reputation: 502

Removing trailing white space only for edited lines

I had added the following function in my .vimrc for removing trailing white spaces just before saving

fun! <SID>StripTrailingWhitespaces()                                            
    let l = line(".")                                                           
    let c = col(".")                                                            
    %s/\s\+$//e                                                                 
    call cursor(l, c)                                                           
endfun                                                                          

autocmd BufWritePre *.h :call <SID>StripTrailingWhitespaces()
autocmd BufWritePre *.cpp :call <SID>StripTrailingWhitespaces()
autocmd BufWritePre *.c :call <SID>StripTrailingWhitespaces()

It works really well. However, in certain cases I would like to remove trailing white spaces only from lines that I have edited. This is to ensure that my diff output looks sane as for certain legacy code files almost all lines have trailing back spaces and I do not want to burden my code reviewer with unnecessary diff.

diff -b is not a solution right now since it also ignores white spaces from anywhere in a line and sometimes that change is important enough to be include in the diff output.

So my question is - is it possible to strip trailing white spaces from only the lines that have been edited in this session in a file in vim?

Upvotes: 14

Views: 2339

Answers (3)

George Hilliard
George Hilliard

Reputation: 15962

mMontu's answer has the right idea, but it doesn't handle an edge case. Namely, if I move the cursor down, then back up, all in edit mode, it doesn't pick up the changes to those lines. If we would like to handle this, then we need to store the top and bottom lines visited by the user. Here is some more robust code, with everything grouped into functions:

fun! <SID>SetupTrailingWhitespaces()
    let curline = line('.')
    let b:insert_top = curline
    let b:insert_bottom = curline
endfun

fun! <SID>UpdateTrailingWhitespace()
    let curline = line('.')
    if b:insert_top > curline
        let b:insert_top = curline
    elseif b:insert_bottom < curline
        let b:insert_bottom = curline
    endif
endfun

fun! <SID>StripTrailingWhitespaces()
    let original_cursor = getpos('.')
    exe b:insert_top ',' b:insert_bottom 's/\s\+$//e'
    call setpos('.', original_cursor)
endfun

Now we just invoke these functions at the right time:

autocmd InsertEnter * :call <SID>SetupTrailingWhitespaces()
autocmd InsertLeave * :call <SID>StripTrailingWhitespaces()
autocmd CursorMovedI * :call <SID>UpdateTrailingWhitespace()

Alternatively, I've written a plugin that provides this updated functionality, with several additional features like stripping in normal mode as well.

Upvotes: 3

KuoE0
KuoE0

Reputation: 1

I write a plugin named 'vim-scavenger' to clean up multiple blank lines and trailing spaces.

Just add the following config in your .vimrc:

let g:scavenger_auto_clean_up_on_write = 1

For more detail, you can come to that Github repo to learn more. Feel free to give me advice to improve the plugin.

Upvotes: 0

mMontu
mMontu

Reputation: 9273

One possibility would be to use autocmd InsertLeave to strip white spaces from current line every time you leave insert mode:

autocmd InsertLeave *.[ch] :call <SID>StripTrailingWhitespaces()

, and change substitute command of StripTrailingWhitespaces() function changed to

s/\s\+$//e

It will work if all lines that you include doesn't end in white spaces, only the last one. It will possible change lines that you didn't modified, if you enter and exit insert mode (i followed by ESC).

This solution can be changed to work if you include lines that does end in white space (pasted lines from legacy code, for example):

autocmd InsertEnter *.[ch] :let b:insert_start = line('.')
autocmd InsertLeave *.[ch] :call <SID>StripTrailingWhitespaces()

fun! StripTrailingWhitespaces()
    let original_cursor = getpos('.')
    exe b:insert_start . ',.s/\s\+$//e'
    call setpos('.', original_cursor)
endfun     

If the replacement on lines due to enter and exit insert mode (i followed by ESC) is a problem then the solution could save b:changedtick-variable when entering insert mode and check it when leaving insert mode to detect changes.

Upvotes: 4

Related Questions