I'd like to trigger an autocmd on two events but not in a way it is usually done, i.e. if either of the events happened then trigger an autocmd. I want to trigger it if both events happened.

For example:
The usual way to do it

autocmd BufWrite,BufRead *.c *.py *.h :call StripTrailingWhitespaces()

This code will call StripTrailingWhiteSpaces() on either BufWrite or BufRead

I would like to do something like:

autocmd Filetype c,cpp,python AND BufWrite :call StripTrailingWhiteSpaces()

In other words trigger an autcmd when the filetype is one of c, cpp, python and the write on this buffer happens.

Any help is appreciated.

up vote 11 down vote accepted

An autocommand command is executed when one event occurs. You want a command to be executed after a sequence of events has occurred. One way to do that is like this:

autocmd FileType c,cpp,python
    \ autocmd BufWritePre <buffer> call StripTrailingWhiteSpaces()

The <buffer> pattern causes the autocommand to be be triggered when the current buffer is written. See

:help autocmd-buflocal

Update

The solution above is pretty simple and has some flaws that were discussed in the Comments. Here is a more complete solution that addresses some of those flaws. It puts the autocommands in a group and deletes the BufWritePre autocommand, if one exists, before creating a new one. It still creates one autocommand per buffer, but only one.

augroup TrailSpace
    autocmd FileType c,cpp,python
        \ autocmd! TrailSpace BufWritePost <buffer> call SkipTrailingWhiteSpaces()
augroup END

Another solution, similar to the answer posted by lcd047, now deleted, is to recognize that when the FileType event occurs, the 'filetype' option is set. Then you can condition the response to the BufWritePost event on the value of 'filetype', as in the following example. It has the advantage over the other solutions that only one autocommand is created.

autocmd BufWritePre * if count(['c','cpp','python'],&filetype)
    \ | call SkipTrailingWhiteSpaces()
    \ | endif
  • What if I want to run this on all the files that are currently open, i.e. I execute :wa? – flashburn Jul 17 '15 at 15:30
  • If the files were opened with the correct filetype, the FileType autocmd in the answer would have already set up the second autocmd (BufWritePre) to fire when you save them. – VanLaser Jul 17 '15 at 15:41
  • 1
    The FileType autocmd above will fire for every file you open with the correct filetype, and will setup a buffer-local event for each of those files. So if you run :wa, vim will run registered events for each buffer, before saving to file. – VanLaser Jul 17 '15 at 15:48
  • 1
    So if you open 5 Python files you'll have 5 autocmds instead of a single one, all on write. Then if, say, 3 of these files get hidden, then get shown again, FileType gets re-triggered so you get 3 more autocmds, also on write. This is brilliant, I wonder why I didn't come up with this solution. :) – lcd047 Jul 17 '15 at 16:59
  • 1
    Performance is not a problem. Running the function stripTrailingWhiteSpaces() several times against the same file might have unintended consequences though. Also, the more autocmds you have for the same event for the same file, the more likely you are to run into some really race conditions. Try searching vim_dev archives to get an idea. Then again, what do I know, it might just work for you, right? – lcd047 Jul 17 '15 at 17:34

More generally, if you don't know which event will happen first, you can use a helper to track when each one fires and only execute your command when the last one fires:

function StripTrailingWhiteSpacesIfReady(event) abort
  if !exists('b:events_for_whitespace')
    let b:events_for_whitespace = {}
  endif
  let b:events_for_whitespace[a:event] = 1
  if has_key(b:events_for_whitespace, 'FileType') && has_key(b:events_for_whitespace, 'Buf')
    " Strip trailing whitespace
    %s/\m\s\+$//
  endif
endfunction
autocmd Filetype c,cpp,python call StripTrailingWhiteSpacesIfReady('FileType')
autocmd BufWrite,BufRead * StripTrailingWhiteSpacesIfReady('Buf')

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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