4

I put the following lines from this page into my ~/.vimrc to highlight trailing whitespace in red:

highlight ExtraWhitespace ctermbg=red guibg=red
match ExtraWhitespace /\s\+$/
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
autocmd BufWinLeave * call clearmatches()

I would like to have this be the default behavior, but be able to toggle this behavior on and off with something like \w. I found a code snippet from this answer that looks promising. After editing it I had something like this:

let g:toggleHighlightWhitespace = 1
function! ToggleHighlightWhitespace(...)
  if a:0 == 1 "toggle behaviour
    let g:toggleHighlightWhitespace = 1 - g:toggleHighlightWhitespace
  endif

  if g:toggleHighlightWhitespace == 1 "normal action, do the hi
    highlight ExtraWhitespace ctermbg=red guibg=red
    match ExtraWhitespace /\s\+$/
    autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
    autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
    autocmd InsertLeave * match ExtraWhitespace /\s\+$/
    autocmd BufWinLeave * call clearmatches()
  else
    call clearmatches()
  endif
endfunction

autocmd BufWinEnter * call ToggleHighlightWhitespace()
autocmd InsertEnter * call ToggleHighlightWhitespace()
autocmd InsertLeave * call ToggleHighlightWhitespace()
autocmd BufWinLeave * call ToggleHighlightWhitespace()
nnoremap <leader>w :call ToggleHighlightWhitespace(1)<cr>

With these lines, typing \w in normal mode gets rid of the highlighting, but as soon as I re-enter insert mode, the highlighting returns. How do I get the toggling to work?

EDIT

Thanks to help from Grochmal, my final snippet from ~/.vimrc is shown below. I split the toggle function into a toggle function and a refresh function for clarity.

let g:toggleHighlightWhitespace = 1    
function! ToggleHighlightWhitespace()    
  let g:toggleHighlightWhitespace = 1 - g:toggleHighlightWhitespace     
  call RefreshHighlightWhitespace()    
endfunction    

function! RefreshHighlightWhitespace()    
  if g:toggleHighlightWhitespace == 1 " normal action, do the hi    
    highlight ExtraWhitespace ctermbg=red guibg=red    
    match ExtraWhitespace /\s\+$/    
    augroup HighLightWhitespace    
      autocmd BufWinEnter * match ExtraWhitespace /\s\+$/    
      autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/    
      autocmd InsertLeave * match ExtraWhitespace /\s\+$/    
      autocmd BufWinLeave * call clearmatches()    
    augroup END    
  else " clear whitespace highlighting    
    call clearmatches()    
    autocmd! HighLightWhitespace BufWinEnter    
    autocmd! HighLightWhitespace InsertEnter    
    autocmd! HighLightWhitespace InsertLeave    
    autocmd! HighLightWhitespace BufWinLeave    
  endif    
endfunction    

autocmd BufWinEnter * call RefreshHighlightWhitespace()    
autocmd BufWinLeave * call RefreshHighlightWhitespace()    
nnoremap <leader>w :call ToggleHighlightWhitespace()<cr>
  • From :help :autocmd: When your .vimrc file is sourced twice, the autocommands will appear twice. To avoid this, put :autocmd! in your .vimrc file, before defining autocommands. See this answer to know how. – Jair López Jul 4 '16 at 0:53
4

Extraneous autocmd

First, you told Vim to call your toggle function every time you enter or leave the insert mode. i.e. by using the InsertEnter and InsertLeave events as follows:

autocmd InsertEnter * call ToggleHighlightWhitespace()
autocmd InsertLeave * call ToggleHighlightWhitespace()

Those events are not needed there. Therefore, kill these two lines.

Long standing autocmd

On the other hand these events are needed in the match sections, because in insert mode you might be typing at spaces at the end of the line:

autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
autocmd InsertLeave * match ExtraWhitespace /\s\+$/

Do not kill these autocmds. But that is not all, these autocmds will trigger match whether your toggle is on or off, which is not what a toggle is intended for.

These autocmds need to be disabled when the toggle is off, but there is no way to disable an autocmd! You need to wrap these autocmds inside an augroup so they are not long standing, i.e. so they can be disabled.

In essence, your condition needs to looks as follows:

if g:toggleHighlightWhitespace == 1 "normal action, do the hi
  highlight ExtraWhitespace ctermbg=red guibg=red
  match ExtraWhitespace /\s\+$/
  augroup HighlightWhitespace
  autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
  autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
  autocmd InsertLeave * match ExtraWhitespace /\s\+$/
  autocmd BufWinLeave * call clearmatches()
  augroup END
else
  call clearmatches()   
  autocmd! HighlightWhitespace BufWinEnter
  autocmd! HighlightWhitespace InsertEnter
  autocmd! HighlightWhitespace InsertLeave
  autocmd! HighlightWhitespace BufWinLeave
endif

autocmd! can only disable an autocmd if it is named by an augroup (I'm not 100% certain of this statement, but it does work well for the script at hand).

  • Deleting those two lines doesn't change the highlighting behavior for me. Re-entering insert mode still turns highlighting back on either way. – Ben Lindsay Jul 3 '16 at 20:19
  • @BenLindsay autocmd has the bad habit of sticking around. Did you exit the vim instance altogether (you should) or just sourced the edited script? Wait, i didn't test it properly. I think i can replicate it with the lines removed. – grochmal Jul 3 '16 at 20:23
  • Yeah, I exited vim altogether. – Ben Lindsay Jul 3 '16 at 20:39
  • @BenLindsay - i was wrong, or better, i tested the solution poorly. You actually need to disable the autocmds for the period your toggle is off (whether in insert or normal mode). I have extensively edited the answer. – grochmal Jul 3 '16 at 20:42
1

Take into account that vim already includes this highlight functionality. The options 'list' and 'listchars' can be used for that purpose by adding the following lines to vimrc:

set list
set listchars=trail:*
  • Thanks, wasn't aware of this. I think the setup I have is a little nicer though, since in insert mode, the line my cursor is on doesn't have highlighting turned on but all other lines do. This is nice because as I'm typing a line, I don't like to have every new space highlighted. – Ben Lindsay Jul 7 '16 at 15:41

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.