How do you use buffer-local autocommands in VimScript?
Asked Answered
T

1

6

I'm trying to write a snippet of VimL to allow the user to toggle the hilighting of unwanted trailing whitespace with a hotkey. (This is my first-ever Vim script, beyond copy-pasting things into my .vimrc, so … grain of salt :P)

I wish for the ‘are we currently hilighting trailing whitespace?’ to be buffer-specific state; but I'm having a lot of trouble figuring out how autocommands interact with buffers.

For instance, here's my first stab at an augroup of buffer-local autocmds:

augroup  ExtraWhitespace
   au!
   au BufEnter    <buffer=abuf> match ExtraWhitespace /\s\+$/
   au InsertEnter <buffer=abuf> match ExtraWhitespace /\s\+\%#\@<!$/
   au InsertLeave <buffer=abuf> match ExtraWhiteSpace /\s\+$/
augroup END

… unfortunately, this immediately trips up when invoked:

Error detected while processing function ToggleExtraWhitespace: 
line   19:
E680: <buffer=0>: invalid buffer number 
line   20:
E680: <buffer=0>: invalid buffer number 
line   21:
E680: <buffer=0>: invalid buffer number 
No matching autocommands

I don't understand why <abuf> is 0, when bufnr('%') is 1, or how to get the autocommands to execute for buffer 1 instead. (Of course 0 is invalid!)


For the moment, I've swapped out <buffer=abuf> for *; but this screws up the functionality of this function when there are multiple buffers loaded, and That Is Bad. So, any help figuring this out is welcome. /=

Taritariff answered 4/8, 2015 at 22:34 Comment(3)
Is there any reason you need <buffer=abuf>? From the snippets you should only need <buffer> . Also how are you loading theses? Is it being loaded during your vimrc?Tractable
Sorry didn't look at all of the links. The answer to the last two are loaded during VimEnter.Tractable
You're making it a lot more complicated than it is. Take a look at trailertrash for a simpler approach that actually works.Outweigh
T
9

First, I don't know how <buffer=abuf> works. The documentation for it seems to be conflicting. It appears the the behavior for <buffer=abuf> was changed/fixed with patch 7.4.637, before It was causing problems even if used correctly. <buffer=abuf> must only be used when an autocmd is running. So your function would probably have worked if you called it in VimEnter or BufAdd.


The following a modified version of what you attempted which doesn't use <buffer=abuf>

  augroup ExtraWhitespace
     autocmd! * <buffer>
     autocmd BufEnter    <buffer> match ExtraWhitespace /\s\+$/
     autocmd InsertEnter <buffer> match ExtraWhitespace /\s\+\%#\@<!$/
     autocmd InsertLeave <buffer> match ExtraWhitespace /\s\+$/
  augroup END

The first things you should notice is that au! has been replaced with autocmd! * <buffer>. au! shouldn't be there since this will remove all autocmd in the ExtraWhitespace group from all buffers. This means that you can only define it in one buffer. (autocmd! * <buffer> only deletes the autocmds in the current buffer)

The second thing you should notice is that <buffer> is used. This means that the autocmd will be created only for the current buffer when when the function is called. Buffer local autocmd must be called for each buffer that you want to define.


Other miscellaneous comments

You have

fun! HighlightExtraWhitespace()
   if exists('b:ews') && b:ews == 1
     "echom "-- Adding ExtraWhitespace hilighting"
      highlight ExtraWhitespace ctermbg=red guibg=red
   else
     "echom "-- Removing ExtraWhitespace hilighting"
      highlight clear ExtraWhitespace
   endif
endfun
au ColorScheme * call HighlightExtraWhitespace()

Highlighting is global, so clearing it in one buffer is going to remove the highlight group everywhere. So its better to just leave the highlighting in place and redefine it every time the colorscheme changes.

autocmd ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red

It is recommended to use the long form of command names in scripts. (Short form used only for typing). The long form is more readable and easily identifiable, so au would be come autocmd.

Tractable answered 5/8, 2015 at 2:1 Comment(7)
How is “using <abuf> (only when executing autocommands)” conflicting? When you do au event <buffer=abuf> in your vimrc you are not executing autocommands. You are executing your vimrc. Of course, this does not work. You need to use au event pattern :au event <buffer=abuf>.Lafreniere
@Lafreniere Then the help for :help <buffer=abuf> needs to be changed. The example usage there is :au CursorHold <buffer=abuf> echo 'hold' however that also gets E680. I also tried the line you suggested with :au BufEnter * :au BufEnter <buffer=abuf> and get the same E680. (I tried posting to vim_use but I think my post got eaten by the spam filter)Tractable
It looks more like you have Vim compiled without this feature. Specifically, my vim which does work with abuf emits the same error message on au CursorHold <buffer=tty> :echo 'hold'. Note: echo +"tty" emits zero, this is the most likely reason for both your and my observations. (Also I have the same E680 when using this feature outside of an autocmd, but inside everything is fine.)Lafreniere
Though hg annotate shows that this feature was added in vim-7.0024 (or, at least, it was documented then).Lafreniere
@Lafreniere I bet ftp.vim.org/vim/patches/7.4/7.4.637 is the reason we see the differences. I was running running up to patch 7.4.622.Tractable
This is looking great. I don't have time right this moment, but as soon as I can test that this works as I was expecting, I'll come back and accept it. <3Taritariff
(Also, excellent, detailed answer. You're the sort that makes Stack Overflow lovely.)Taritariff

© 2022 - 2024 — McMap. All rights reserved.