Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The right way to override any highlighting if you don't want to edit the colorscheme file directly

The right way to override any highlighting if you don't want to edit the colorscheme file directly

Suppose you have weird taste and you absolutely want:

  • your visual selection to always have a green background and black foreground,
  • your active statusline to always have a white background and red foreground,
  • your very own deep blue background.

Your first reflex is probably to put those lines somewhere in your vimrc:

highlight Visual cterm=NONE ctermbg=76 ctermfg=16 gui=NONE guibg=#5fd700 guifg=#000000
highlight StatusLine cterm=NONE ctermbg=231 ctermfg=160 gui=NONE guibg=#ffffff guifg=#d70000
highlight Normal cterm=NONE ctermbg=17 gui=NONE guibg=#00005f
highlight NonText cterm=NONE ctermbg=17 gui=NONE guibg=#00005f

But it quickly appears that those lines have no effect if you source a colorscheme later in your vimrc so you move them below:

colorscheme foobar
highlight Visual cterm=NONE ctermbg=76 ctermfg=16 gui=NONE guibg=#5fd700 guifg=#000000
highlight StatusLine cterm=NONE ctermbg=231 ctermfg=160 gui=NONE guibg=#ffffff guifg=#d70000
highlight Normal cterm=NONE ctermbg=17 gui=NONE guibg=#00005f
highlight NonText cterm=NONE ctermbg=17 gui=NONE guibg=#00005f

which gives the desired outcome. Everything is fine. In principle.

But you like to try new colorschemes, or you prefer to have different colorschemes for different filetypes or time of the day and your overrides don't carry over when you change your colorscheme.

This is because colorschemes usually reset all highlighting, including your own, when they are sourced.

The solution is to override the desired highlights in an autocommand that's executed whenever a colorscheme is sourced:

augroup MyColors
    autocmd!
    autocmd ColorScheme * highlight Visual cterm=NONE ctermbg=76 ctermfg=16 gui=NONE guibg=#5fd700 guifg=#000000
                      \ | highlight StatusLine cterm=NONE ctermbg=231 ctermfg=160 gui=NONE guibg=#ffffff guifg=#d70000
                      \ | highlight Normal cterm=NONE ctermbg=17 gui=NONE guibg=#00005f
                      \ | highlight NonText cterm=NONE ctermbg=17 gui=NONE guibg=#00005f
augroup END
colorscheme foobar

Note that the autocmd must be added before any colorscheme is sourced or it will have no effect.

Of course, we can go one step further and put all our highlights in a neat little function for maximum readability/managability/maintainability:

function! MyHighlights() abort
    highlight Visual     cterm=NONE ctermbg=76  ctermfg=16  gui=NONE guibg=#5fd700 guifg=#000000
    highlight StatusLine cterm=NONE ctermbg=231 ctermfg=160 gui=NONE guibg=#ffffff guifg=#d70000
    highlight Normal     cterm=NONE ctermbg=17              gui=NONE guibg=#00005f
    highlight NonText    cterm=NONE ctermbg=17              gui=NONE guibg=#00005f
endfunction

augroup MyColors
    autocmd!
    autocmd ColorScheme * call MyHighlights()
augroup END
colorscheme foobar

NOTE: The autocommands above use the glob * in order to override any colorscheme. If, say… you really like the colorscheme foobar but you can't stand its Visual highlight, you will want your autocommand to target that colorscheme specifically. To this end, you can use the name of that colorscheme instead of *:

autocommand ColorScheme foobar call MyHighlight()

NOTE: This won't work if you have an autocommand like the following in your vimrc because autocommands don't nest by default:

[...]
autocmd BufWritePost $MYVIMRC source $MYVIMRC
[...]

The solution is to add the nested keyword to that autocommand to allow other autocommands to be triggered by that event:

[...]
autocmd BufWritePost $MYVIMRC nested source $MYVIMRC
[...]

My Vim-related gists.

@m3hdad

This comment has been minimized.

Copy link

@m3hdad m3hdad commented Jul 25, 2019

Thank you

@davidmathers

This comment has been minimized.

Copy link

@davidmathers davidmathers commented Sep 23, 2019

"nested" is precisely what I didn't know I was searching for. Thanks!

@wingr

This comment has been minimized.

Copy link

@wingr wingr commented Feb 12, 2020

Thank you! This was driving me crazy

@openjck

This comment has been minimized.

Copy link

@openjck openjck commented Apr 11, 2020

Can you say more about nesting? I'm not sure I understand why it's needed here. The Vim documentation says that it's needed when an autocmd calls :e or :w.

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Apr 11, 2020

@openjck, by default, autocommands don't trigger other autocommands. In the classic example I use in the "article", an autocommand is set to :source $MYVIMRC automatically when you write it. The problem, here, is that you are very likely to have a line like this one somewhere in your vimrc:

colorscheme whatever

which, when it is executed as part of the re-sourcing of your vimrc, won't trigger the autocommand described in the article because of that "no nesting" rule. Adding the nested keyword allows autocommands to trigger other autocommands and thus our ColorScheme autocommand to be triggered by the BufWritePost autocommand.

@openjck

This comment has been minimized.

Copy link

@openjck openjck commented Apr 12, 2020

@romainl That makes sense. Thank you for the detailed response!

@Ulver56

This comment has been minimized.

Copy link

@Ulver56 Ulver56 commented Jun 16, 2020

Thank you!

@justrajdeep

This comment has been minimized.

Copy link

@justrajdeep justrajdeep commented Sep 24, 2020

Thanks

@woobhurk

This comment has been minimized.

Copy link

@woobhurk woobhurk commented Oct 28, 2020

Well, it works!
My highlight settings always override by other unknown settings, which makes me annoyed. Thanks very much!

@llinfeng

This comment has been minimized.

Copy link

@llinfeng llinfeng commented Nov 28, 2020

I find this solution shall fail to tune the TabLine colors when vim-airline is at play. Without offloading vim-airline altogether, specifying hi! link airline_tabfill VertSplit (or other highlight groups) serves as a variable way to tune the color for the TabLine highlight group.

@romainl

This comment has been minimized.

Copy link
Owner Author

@romainl romainl commented Nov 29, 2020

@llinfeng I would see that as a sign of bad design on vim-airline's side, not as a failure of this method.

@llinfeng

This comment has been minimized.

Copy link

@llinfeng llinfeng commented Nov 29, 2020

@romainl, I enjoyed reading about catching the ColorScheme triggering condition and reloading customized highlight groups when the color scheme changes. Yet, as with the TabLine highlight group (or the airline_tabfill from vim-airline), there are other cases where highlight groups are administrated by a third-party plugin. I also agree with your comment on the "bad design" aspect.

@YashKarthik

This comment has been minimized.

Copy link

@YashKarthik YashKarthik commented Dec 31, 2020

thank you 🙏

@ri-cisse

This comment has been minimized.

Copy link

@ri-cisse ri-cisse commented May 6, 2021

Thanks!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment