Modify Vim syntax files for your tasteMon 17 August 2020
In this short how-to, we'll see how to make small modifications to a Vim syntax file, in order to change how a particular file format is highlighted. We'll go for a simple use-case: modify the Markdown syntax file, so that H1 and H2 headings (titles and subtitles, if you prefer) are displayed in bold. Of course, this won't be exactly as easy as expected, but no worries, we'll succeed in the end.
I'm mostly happy with that, except for one or two little details. I'd like to have the titles displayed in bold, for example, so that they're easier to spot when I skim through a Markdown file. It seems like a simple thing to ask, so I hope there can be a simple solution.
The first steps
Let's learn the basics.
In Vim world, the rules to highlight files formats are defined in the directory
/usr/share/vim/vim82/syntax (I bet you'll have to adjust this path depending
on the version of Vim that is installed on your system).
And so, for the Markdown file format, the rules are defined in the file
The first thing we could do is to have a look at this file, try to make sense of it, and maybe start to make some modifications.
But wait a moment. You should know that modifying a system file is not a great idea. First because your changes will be lost as soon as an update kicks in and the package manager replaces this file by a new version. Second, because you will quickly forget what files you modified, and what were your modifications, and if you do that too much, you might experience what is called "maintenance headache" in the long run.
So instead, maybe you DO NOT modify this file, and instead you copy it in your
personal Vim folder, more precisely in
~/.vim/syntax. Create this directory
if it does not exist:
mkdir -p ~/.vim/syntax cp /usr/share/vim/vim82/syntax/markdown.vim ~/.vim/syntax
The file in your personal folder takes precedence over the system file of the
same name in
/usr/share/vim/vim82/syntax/, it is a replacement for the
existing syntax files. And so from now on, Vim uses the file
~/.vim/syntax/markdown.vim, and this is where we can make our modifications.
(And by the way, this is explained in the Vim faq-24.12)
And so, it's already nice to know all of that, but wait, there's even better.
There's is another location of interest, and it is
can drop syntax files in this directory, and these files are treated as
additions to the existing syntax. So if you only want to make slight
modifications, that's the way to go.
(And by the way, this is explained in the Vim faq-24.11)
So let's forget about a syntax replacement in
instead let's go for some syntax additions in
mkdir -p ~/.vim/after/syntax touch ~/.vim/after/syntax/markdown.vim
Now, let's answer the initial question: how do we modify the highlighting rules
for Markdown files, so that the titles are displayed in bold? First, we have to
understand where are the rules that define the highlighting for titles. Here
there are, from the file
hi def link markdownH1 htmlH1 hi def link markdownH2 htmlH2 hi def link markdownH3 htmlH3 ...
You should know that H1 means Heading 1, and so on, and so we want to make
H1 and H2 bold. What we can see here is that the headings in the Markdown files
are highlighted like the headings in HTML files, and this is obviously defined
in the file
/usr/share/vim/vim82/syntax/html.vim. So let's have a look into
hi def link htmlH1 Title hi def link htmlH2 htmlH1 hi def link htmlH3 htmlH2 ...
Let's keep digging a bit. Where is
Title defined? For those using the
default color scheme like me, this is defined straight in the Vim source
code, in the file
CENT("Title term=bold ctermfg=DarkMagenta", "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
And for those using custom color schemes, it might be defined in a file under
Alright, so how do we override that? We can just define this kind of rules in
our syntax additions file at
hi link markdownH1 markdownHxBold hi link markdownH2 markdownHxBold hi markdownHxBold term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta cterm=bold
As you can see, the only addition we made, compared to what's defined in
cterm=bold. And that's already enough to achieve the
initial goal, make the titles (ie. H1 and H2) bold. The result can be seen in
the following screenshot:
The rabbit hole
So we could stop right here, and life would be easy and good.
However, with this solution there's still something that is not perfect. We use
DarkMagenta as defined in the default color scheme. What I didn't
mention however, is that this is applicable for a light background. If you
have a dark background though, dark magenta won't be easy to read.
Actually, if you look a bit more into
src/highlight.c, you will see that the
default color scheme comes in two variants, one for a light background, and one
for a dark background.
And so the definition for
Title for a dark background is as follow:
CENT("Title term=bold ctermfg=LightMagenta", "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
Hmmm, so how do we do that in our syntax file? How can we support both light and dark background, so that the color is right in both cases?
After a bit of research, and after looking at other syntax files, it seems that
the solution is to check for the value of the
background option, and so our
syntax file becomes:
hi link markdownH1 markdownHxBold hi link markdownH2 markdownHxBold if &background == "light" hi markdownHxBold term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta cterm=bold else hi markdownHxBold term=bold ctermfg=LightMagenta gui=bold guifg=Magenta cterm=bold endif
In case you wonder, in Vim script you prefix Vim options with
&, and so you
get the value of the
background option by writing
&background. You can
learn this kind of things in the Vim scripting cheatsheet.
And so, it's easy enough, except for one thing: it doesn't work. The headings
always show up in
DarkMagenta, even for a dark background.
This is why I called this paragraph "the rabbit hole", by the way.
So... Well after trying a few things, I noticed that in order to make it work,
I would have to reload the syntax files with
At this point, the most likely explanation is that the
background option is
not set yet when the syntax files are loaded at startup, hence it needs to be
reloaded manually afterward.
And after muuuuuuch research, I found out that it's actually possible to set a
hook for when an option is modified. Meaning, it's possible to execute a
function when the
background option is modified. Quite cool actually.
And so, there it goes in my
" Reload syntax when the background changes autocmd OptionSet background if exists("g:syntax_on") | syntax on | endif
For humans, this line reads as:
- when the background option is modified --
autocmd OptionSet background
- check if the syntax is on --
- if that's the case, reload it --
With that in place, my Markdown syntax overrides work for both dark and light background. Champagne!
The happy end
To finish, let me share my actual additions to the
markdown.vim syntax. It
makes H1 and H2 bold, along with their delimiters, and it also colors the
inline code and the code blocks.
" H1 and H2 headings -> bold hi link markdownH1 markdownHxBold hi link markdownH2 markdownHxBold " Heading delimiters (eg '#') and rules (eg '----', '====') -> bold hi link markdownHeadingDelimiter markdownHxBold hi link markdownRule markdownHxBold " Code blocks and inline code -> highlighted hi link markdownCode htmlH1 " The following test requires this addition to your vimrc: " autocmd OptionSet background if exists("g:syntax_on") | syntax on | endif if &background == "light" hi markdownHxBold term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta cterm=bold else hi markdownHxBold term=bold ctermfg=LightMagenta gui=bold guifg=Magenta cterm=bold endif
And here's how it looks like with a light background:
And a dark background:
That's all, that's very little changes compared to the highlighting from the original syntax file, and now that we understand how it's supposed to be done, it's not much effort to achieve it.
It's just that finding the workaround to make it work for both light and dark background took forever, and leaves the usual, unanswered question: bug or feature?