3

Consider the following vimscript:

function! DecideWhetherToStrip()
    let l:search = @/
    " Check if a file being opened has trailing spaces in the first place.
    let b:dostrip = !search('\s\+$', 'npw')
    let @/ = l:search
endfunction

function! StripTrailingWhitespace()
    if b:dostrip
        let l:search = @/
        let l:winview = winsaveview()
        %s/\s\+$//e            " Kill spaces at EOL
        %s/\($\n\s*\)\+\%$//e  " Kill lines at EOF
        let @/ = l:search
        call winrestview(l:winview)
    endif
endfunction

augroup whitespace
    autocmd!
    autocmd BufReadPost * call DecideWhetherToStrip()
    autocmd BufWritePre * call StripTrailingWhitespace()
augroup END

The StripTrailingWhitespace function is probably familiar to many people as I've mostly stolen it from the internet, but the DecideWhetherToStrip and the b:dostrip variable were put together by me, just now.

The intention of this code is that I want to detect whether a newly opened file contains trailing whitespace, and then only strip trailing whitespace if the file didn't have any to begin with. This is because I need to edit certain files that actually require trailing whitespace, but for the most part I want files to start clean and stay clean.

My question is: does this code work the way I think it does?

It seems to work in my preliminary testing but I'm worried that there will be unexpected surprises down the line, in certain corner cases. Like what if I have two files open, one with b:dostrip set to 1, and one 0. How does StripTrailingWhitespace know which buffer it's operating on? What if two files are modified and I call :wall!, is autocmd smart enough to call StripTrailingWhitespace once each with each buffer active? Or will I run into issues where StripTrailingWhitespace will only be called once with the setting from one buffer but will actually modify both buffers?

Are there any other failure modes to this code I should be wary of?

Thanks.

  • 4
    b:foo is local to whatever buffer is the current buffer and yes, your autocommands will run for every file. – romainl Sep 8 '15 at 7:29
2

if I have two files open, one with b:dostrip set to 1, and one 0. How does StripTrailingWhitespace know which buffer it's operating on?

b: always relates to the currently active buffer. It's exactly the same as function-local (foo) or class-local (self.foo) variables that exist is most programming languages: it's always local to the current function, never local to another function (you can still access buffer-local variables from other buffers by the way, but not with the b: syntax; you need the getbufvar() function for that).

See :help b: for the documentation by the way.

What if two files are modified and I call :wall!, is autocmd smart enough to call StripTrailingWhitespace once each with each buffer active?

Yes. The Buf* events are run for every bufffer.

will I run into issues where StripTrailingWhitespace will only be called once with the setting from one buffer but will actually modify both buffers?

No. Vim commands always operate on the current buffer except for a few commands that work on multiple buffers (like :bufdo). Remember that Vim commands are exactly the same as what you're doing in Vim whenever you're editing some file. There's little "magic" here.

  • You mentioned that you can access buffer-local variables from other buffers. How exactly is this done? – jobo3208 Mar 3 '16 at 23:57
  • @jobo3208 You can use the getbufvar() function. In the first argument you tell it which buffer to use. – Martin Tournoij Mar 3 '16 at 23:59
  • Awesome, couldn't find that anywhere. Thanks! – jobo3208 Mar 4 '16 at 0:00
  • @jobo3208 I couldn't remember the name from the top of my head either, but I found it in :help function-list and searching for buffer ;-) – Martin Tournoij Mar 4 '16 at 0:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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