4

I want all trailing whitespace in the buffers I'm editing to be highlighted in red.

Here is the autocommand I'm using:

autocmd BufEnter,WinEnter * call matchadd('Error', '\v\s+$', -1)

The matchadd() function looks for the pattern '\v\s+$' which describes trailing whitespace, and changes its color with the highlighting group Error and a -1 priority (so that 'hlsearch' whose priority is 0 can override it).

I would like to modify it so that the command is not executed in a help file. I've come up with this :

autocmd BufEnter,WinEnter * if &filetype !=? "help"
            \ | call matchadd('Error', '\v\s+$', -1)
            \ | endif

However, trailing whitespace is still colored in red even in a help file. For example, in :help 'relativenumber' below the line :

See hl-LineNr and hl-CursorLineNr for the highlighting used for the number.

... I see a red tab character on an empty line.

I've commented the autocommand, and tested the following command in various buffers (help and non help) :

if &filetype !=? "help" | call matchadd('Error', '\v\s+$', -1) | endif

It works as expected :
trailing whitespace in red in a non help file, trailing whitespace uncolored in a help file.

How to modify the autocommand so that the matchadd() function is not called in a help file ?

saginaw
  • 6,756
  • 2
  • 26
  • 47
  • 1
    Perhaps (also) check the other way around: if it is a help file, remove the match. I think a help file is "visited" twice by Vim: first the file is opened, then a tag jump takes place, which would account for a 2nd "visit". At least, as far as I know, some autocmds get executed twice for help buffers. – VanLaser Dec 07 '15 at 11:38

1 Answers1

2

I didn't realise that matchadd() was piling up the same highlight pattern (just with a different, incremented id), the more I was opening new buffers or simply switching between windows. Here was the output of echo getmatches() after just a few opened buffers and switching between windows:

[{'group': 'Error', 'pattern': '\v\s+$', 'priority': -1, 'id': 4}, {'group': 'Error', 'pattern': '\v\s+$', 'priority': -1, 'id': 5}, {'group': 'Error', 'pattern': '\v\s+$', 'priority': -1, 'id': 6}, ..., {'group': 'Error', 'pattern': '\v\s+$', 'priority': -1, 'id': 24}]

I suspect the only thing that my modified autocmd was doing is preventing a new match to be added when entering a help buffer. But it didn't remove all the previous ones, so maybe that's why a trailing whitespace was still colored in red even in a help buffer. Or maybe not. Maybe the highlighting is local to the window, so the previous ones were not affecting a help buffer displayed in a new window. I don't know. (*)

Besides, I wonder if the piling of the same matches would have an effect performance-wise in the long run...

Anyway, I've found a workaround using the Ex command :2match instead of the function matchadd(). Here's the code:

augroup trailing_whitespace
    autocmd!
    autocmd BufEnter,WinEnter * 2match Error /\s\+$/ |
                              \ if &filetype ==# 'help' |
                              \ call matchdelete(2) |
                              \ endif
augroup END

The advantage of :2match compared to matchadd() is that it doesn't pile up new matches, it just recreates the same one with the same id, 2, every time a buffer/window is entered.

To prevent the match to be created in a help buffer, the autocmd also executes call matchdelete(2) to delete the match whose id is 2 when the filetype is 'help' (thank you VanLaser for giving me the idea).

So far it seems to work as intended. Trailing whitespace in red everywhere except in a help buffer.

As a side note, there are actually 3 similar commands: :match, :2match, :3match which create a match whose id is respectively 1, 2 and 3.
I didn't use :match because it's more convenient to keep it untouched in case you want to create a quick match temporarily.
And I couldn't use :3match because it was already used by the script-local function Highlight_Matching_Pair() defined in /usr/share/vim/vim74/plugin/matchparen.vim.


(*) I'm not sure, but I think the matches created by matchadd() in previous windows did have an effect on a help buffer even when displayed in a new window. Otherwise the following code would work:

augroup trailing_whitespace
    autocmd!
    autocmd BufEnter,WinEnter * let m = matchadd('Error', '\v\s+$', -1) |
                              \ if &filetype ==# 'help' |
                              \ call matchdelete(m) |
                              \ endif
augroup END

But it doesn't work. A trailing whitespace is still colored in red even in a help buffer displayed in a new window.

Edit: I've just realised I could pass a fourth optional argument to matchadd() to specify the id I want, which would prevent the piling up of identical matches. So I could write something like:

augroup trailing_whitespace
    autocmd!
    autocmd BufEnter,WinEnter * call matchadd('Error', '\v\s+$', -1, 9999) |
                              \ if &filetype ==# 'help' |
                              \ call matchdelete(9999) |
                              \ endif
augroup END

But it doesn't work as intended. Besides, every time I would open a buffer or switch to another window, I would have the following error:

Error detected while processing BufEnter Auto commands for "*":
E801: ID already taken: 9999

matchadd() doesn't want to create a match whose id is already used while :2match doesn't care. So all in all, it seems :2match is a better solution for this particular autocmd.

saginaw
  • 6,756
  • 2
  • 26
  • 47