About

In this post I’m going to briefly describe how to contribute to my color schemes. All of my color schemes share a very similar code base, so I’m going to take one of these color schemes gruvbox-material for example in this post.

Code Structure

If you want to contribute to gruvbox-material, the very first thing you need to do is to copy /.githooks/pre-commit to /.git/hooks/ after a git clone. This hook is used to update s:last_modified variable in /colors/gruvbox-material.vim.

So what is s:last_modified used for? Well, the idea behind it is as follows:

As you may already know, a color scheme needs to be optimized for a specific plugin or file type to make it look better. This color scheme has been optimized for over 100 plugins and file types, which makes the code base extremely large, that will slow down your vim startup.

Can we load the optimization code on demand? Yes, we can take advantage of syntax files. The syntax files will be loaded after you open a new file type. For example, when you open a new *.c file, vim will load the code in $VIMRUNTIME/syntax/c.vim and $VIMRUNTIME/syntax/c/*.vim, then it will load the code in $VIMRUNTIME/after/syntax/c.vim and $VIMRUNTIME/after/syntax/c/*.vim.

So we can do something like this:

Take vim-plug for example, vim-plug will open a buffer to display a dashboard, the file type of this buffer is vim-plug, so we can place the optimization code of vim-plug in $VIMRUNTIME/after/syntax/vim-plug/gruvbox_material.vim.

For ease of installation and maintenance, instead of creating tons of syntax files, we choose to place all the optimization code in one file /colors/gruvbox-material.vim and use a function to generate the syntax files, this behavior is controlled by option g:gruvbox_material_better_performance. When this option is enabled, this color scheme will generate the syntax files if they don’t exist, and when it’s disabled, this color scheme will delete the syntax files if they exist.

We also need to update the syntax files if something has changed, so we defined a variable s:last_modified to check if the generated syntax files are up to date. If it’s not, this color scheme will regenerate the syntax files.

That’s why we need to update s:last_modified before committing our code, we need to tell this color scheme that something has changed, please regenerate the syntax files.

But in this way, we still cannot load all the optimization code on demand, because many plugins don’t create their own file types (e.g. nvim-treesitter), thus we can’t use syntax files to place our code, we have to load them on every startup.

In practical, when vim loads this color scheme, we first initialize some variables and vim settings, then we use gruvbox_material#highlight() to create highlight groups and use highlight! link command to link highlight groups. We do so for all the highlight groups that can’t be placed in syntax files. When comes to highlight groups that can be placed in syntax files, we first check if g:gruvbox_material_better_performance is enabled, if it’s enabled we regenerate the syntax files if necessary and abort, otherwise we load all the code.

For detailed implementation, please read the code.

Color Palette

The color palette is placed in /autoload/gruvbox_material.vim. I’m not going to put all the colors here, if you don’t know what they look like, install a plugin that display color code (e.g. coc-highlight) and then open this file using your editor.

Here I’m going to introduce how to use these colors.

First, I need to introduce two concepts: primary colors and secondary colors.

Primary colors are the colors that can be widely used in a color scheme, for example this color scheme is warm-toned, so orange is one of the primary colors because orange is warm-toned and can be widely used, while cool colors like blue should not be widely used. The colors that should not be widely used and is used to embellish the entire color scheme (e.g. blue in this color scheme) are secondary colors.

When you optimize a plugin, you should make sure they mainly use primary colors.

Here, I’m going to list the primary colors and the syntax highlighting logic in all of my color schemes.

Gruvbox Material

Color reference: /autoload/gruvbox_material.vim

Primary colors: red, orange, yellow, green

Secondary colors: aqua, blue, purple

Syntax highlighting logic:

  • Red: keywords
  • Orange: operators, modifiers
  • Yellow: types, classes
  • Green: functions, methods
  • Aqua: strings, characters
  • Blue: properties, members
  • Purple: values, includes, preproc, special variables (e.g. self, this)
  • White: variables, constants

Everforest

Color reference: /autoload/everforest.vim

Others are the same as Gruvbox Material.

Sonokai

Color reference: /autoload/sonokai.vim

Primary colors: red, green, blue

Secondary colors: orange, yellow, purple

Syntax highlighting logic:

  • Red: keywords, operators, modifiers
  • Orange: properties, members
  • Yellow: strings, characters
  • Green: functions, methods
  • Blue: types, classes
  • Purple: values, includes, preproc, special variables (e.g. self, this)
  • White: variables, constants

Edge

Color reference: /autoload/edge.vim

Primary colors: red, blue, purple

Secondary colors: yellow, green, cyan

Syntax highlighting logic:

  • Red: variables, constants
  • Yellow: types, classes
  • Green: strings, characters, values
  • Cyan: properties, members, special variables (e.g. self, this)
  • Blue: functions, methods
  • Purple: keywords, operators, modifiers, includes, preproc

How to optimize a plugin?

First of all, you need to find the highlight group of the element you want to optimize.

Tree-sitter

To get the highlight group under current cursor, install nvim-treesitter/playground and execute command :TSHighlightCapturesUnderCursor.

All available highlight groups are listed in https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md.

Semantic highlighting in coc.nvim

Use command :CocCommand semanticTokens.inspect to inspect highlight group under current cursor.

Note that you don’t need to define all available highlight groups, because by default coc will link them to tree-sitter highlight groups, so only optimize the highlight groups you think are unreasonable.

Classic highlighting engine

When you disabled tree-sitter and semantic highlighting engines like coc.nvim, vim will fallback to use classic highlighting engine that is implemented via regex.

As described above, highlight groups used in classic highlighting engine can be divided into two categories:

  • Highlight groups that are defined in syntax files and are used in a specific file type (e.g. pangloss/vim-javascript).
  • Highlight groups that are not defined in syntax files and will take effect in a UI element (e.g. justinmk/vim-sneak).

For the first category, to get the highlight group under current cursor, refer to this answer: How to get group name of highlighting under cursor in vim?

For the second category, the highlight groups are usually listed in help docs or README.

Customize highlight groups

Now you have found the highlight groups you want to optimize, to customize them, you need to edit the color scheme file /colors/gruvbox-material.vim.

To create a new highlight group, call gruvbox_material#highlight() function.

There are also some predefined highlight groups like Red, Orange, Yellow, etc. You can link your highlight groups to these predefine highlight groups.

Note that linking highlight groups is preferable, because linking a highlight group is much faster than creating a new highlight group via gruvbox_material#highlight(). So for better performance, please use highlight linking if possible.

Another thing you need to pay attention to is that when optimizing the first category of highlight groups highlighted by classic highlight engine (i.e. those defined in syntax files), you should place the code in the latter part of this file so they can be loaded on demand.