I started off with the following line in my vimrc:

call matchadd(...)

This applies a highlight to the current window. However, if I split a new window, that window will not have the highlight applied to it. I want the highlight to be applied to every new window as well.

This is what I currently have in my vimrc:

autocmd BufWinEnter * let w:matchId = matchadd(...)
autocmd BufWinLeave * call matchdelete(w:matchId)

This gets the job done. However, it can lead to duplicate entries in the list of highlights given by getmatches(). The issue is that BufWinLeave does not trigger if the buffer that is being left is open in another window. As such, its entry does not get deleted.

I've tried using similar events to trigger the matchadd() and matchdelete() actions, and this is as close as I've gotten to the desired functionality.


If I understand correctly, you have the highlight applied to all windows, and not deleted.

So, you have two scenarios:

  1. A highlight is already active and you open a new window.

    As the window is opened, you'll jump to it. WinEnter shall be triggered. Thus, you just have to know whether the window is opened for the first time or not. You already have w:matchId for that. Just test for its existence ->
    autocmd WinEnter * if !exists('w:matchId') | let w:matchId = matchadd(...) | endif

  2. You have several windows already opened and want to add a new highlight.

    Use what I've written in my initial answer to loop over all the windows. If the window already has a w:matchId defined, you'll need to delete it. And finally, record matchadd() result into w:matchId.

If you want to support several highlight simultaneously, you'll need to have a global dictionary of highlights, and for each highlight, you'll need to associate it to a pair of window id (not a window number! -> win_getid() -- there is code to emulate it on old versions of vim in a old Q/A on SO, and in my lh-vim-lib) and matchadd result or order to keep track of the match added in case you may want to remove them.

Below: My initial answer, before the edit.

You could

  • simply execute :windo call whatever(....),
  • or
    • record the current window id with win_getid(),
    • then jump manually, with wincmd w, to each window in between 1 and winnr('$') to call your function
    • and finally return the window where you were.
  • I think OP wants to run only on any one window of multiple windows to the same buffer.
    – muru
    May 31 '16 at 14:57
  • @muru, Then, the function needs to be called only on visible buffers (instead on all windows), the window list becomes: sort(filter(map(range(1, bufnr('$')), 'bufwinnr(v:val)'), 'v:val >= 0')). But I'm not sure this is what he's looking for either as matchadd() is window-wise, not buffer-wise -- i.e. when executed on a buffer in a window, it has no effect on the same buffer when displayed in another window. May 31 '16 at 15:37
  • I've clarified my question. I wrote it late last night and left out many details. It should hopefully be more clear what I'm going for.
    – fvgs
    May 31 '16 at 21:01
  • I think your example in scenario 1 should work for what I want. In fact, w:matchId can just be a boolean since we don't need to use the return value from matchadd() for anything.
    – fvgs
    Jun 1 '16 at 20:17
  • The only caveat I see with this method is it involves checking the existence of a variable every time a window is entered. This seems like an unnecessary cost given that all I want to do is declare that every window should have this effect. Is there no declarative way to do so? Just as with my method above, this seems like a roundabout way to achieve a functionality that should be declarative, rather than based on events and side effects.
    – fvgs
    Jun 1 '16 at 20:20

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.