I have the following function to remove every trailing white space from the document, without changing my cursor position

function! StripTrailingWhitespaces()
  let l = line('.')
  let c = col('.')
  call cursor(l, c)

I'd like to change it so that it doesn't remove any whitespace to the left of my current cursor position. For example consider this situation, (a _ stands for a space and | is the cursor position)


there's the string test followed by three spaces, and the cursor is between the second and the third space. If I execute that function, I end up with


and all the spaces have been removed, instead I'd like to end up with


where only the space to the right of the current cursor position has been removed (all the other trailing whitespaces throughout the document should be removed in both cases, I only want to change the behaviour on the current line).

How can this be done?

2 Answers 2


You can use the zero-width \%# to match the cursor position, so something like


on the current line, and your other pattern on the other lines:


Unfortunately, by the time either :substitute is happening, the cursor position has moved. So let's save it and make the matches more exact with \%123l and \%123c for matching exact lines and columns:

let [_, line, col, _, _] = getcurpos()
execute printf('global/\%%%dl/substitute/\%%%dc\s\+$//e', line, col)
execute printf('vglobal/\%%%dl/substitute/\s\+$//e', line)

But now that we already have the line number, we don't need the first :global:

let [_, line, col, _, _] = getcurpos()
execute printf('%d substitute/\%%%dc\s\+$//e', line, col)
execute printf('vglobal/\%%%dl/substitute/\s\+$//e', line)

Then you can surround it with winsaveview() and winrestview() to handle the cursor position.

PS technically the first substitute does not need the line number, but being explicit does not hurt.

  • This version actually worked in my testing
    – D. Ben Knoble
    May 27, 2020 at 18:52
  • That moves the cursor to the end of the document, but even after adding a call cursor(line, col), the cursor gets moved one space to the left with every save.
    – noibe
    May 27, 2020 at 19:20
  • @noibe yeah I didn't address the part about restoring the cursor; I figured you had that figured out already. You could try passing col+1 to the first printf and see if that helps? You may also be interested in winsaveview() and winrestview()
    – D. Ben Knoble
    May 27, 2020 at 19:32
  • That fixed it, thanks a lot.
    – noibe
    May 27, 2020 at 19:33
  • 1
    @ChristianBrabandt yeah i know about the first substitute; consider it defensive programming :) at the time i was struggling with global moving the cursor to the beginnings of lines and wanted to be absolutely explicit with my intentions.
    – D. Ben Knoble
    May 28, 2020 at 12:44

You can do it with several substitutions.

" bookmark current position
normal! m`
" remove whitespace on the current line
silent! keepj s/\%#\s\+$//
" same for lines above
silent! keepj 1,- s/\s\+$//
" same for lines below
silent! keepj ''+,$ s/\s\+$//
" restore cursor position
normal! g``
  • Hm, using relative ranges was clever. Nit: :normal! m is the same as :mark or :k.
    – D. Ben Knoble
    May 27, 2020 at 22:49
  • @D.BenKnoble Not exactly. :mark as all Ex-commands is linewise, but we want to save also the column number.
    – Matt
    May 28, 2020 at 6:44
  • doh! You’re correct.
    – D. Ben Knoble
    May 28, 2020 at 12:45

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.