Search code examples
vim

How to add additional syntax highlighting in vim without sudo


When vim is used to write python codes, some keywords in the comments are highlighting with different colors. And the keywords can be found in /usr/share/vim/vim80/syntax/python.vim, where there is a line:

syn keyword pythonTodo      FIXME NOTE NOTES TODO XXX contained

so the words FIXME, NOTE, NOTES, TODO, XXX in the comments will be highlighted.

Now I want to add more words in it, such as adding DEBUG, WARNING but I don't have sudo so I can not directly edit this file. Is there anyway to solve it? What if I want to specify the color for different words? For example, red for DEBUG, yellow for WARNING.


Solution

  • Syntax highlighting is a two-steps process where each step is handled by separate scripts.

    1. The syntax script is in charge of making sense of the syntax of the language: what constitutes a keyword, what constitutes a constant, etc.
    2. The color scheme is in charge of defining how those keywords, constants, etc. look.

    Now, Vim uses two "runtime directories":

    • the "system" one, at /usr/share/vim/vimXX/ for you, which you shouldn't touch, sudo or not,
    • the "user" one, at $HOME/.vim/ for you, where all your customization is supposed to take place.

    Both directories have the same structure so the idea is that you can put your custom stuff there and, as long as it respects the default structure, it will be picked up by Vim automatically.

    In this case, your job is to write your own syntax script at $HOME/.vim/syntax/python.vim, with the following content:

    syn keyword pythonTodo DEBUG WARNING contained
    

    which would give you something like that in a new Vim session:

    keywords

    But then comes your second requirement:

    What if I want to specify the color for different words? For example, red for DEBUG, yellow for WARNING.

    The syntax script doesn't care about colors, only about syntax. It is the color scheme that handles colors but it depends on the hard work of the syntax script. If DEBUG and WARNING are both pythonTodo, then they will both be highlighted the same way.

    This is a bit more complex, but not by much. Let's do it step-by-step.

    1. Define a different highlight group, say pythonTodoAlt, only for WARNING:

      syn keyword pythonTodo DEBUG contained
      syn keyword pythonTodoAlt WARNING contained
      

      Do you see that contained at the end? It means that the highlight group is to be found in other highlight groups that explicitly "contain" it. In this case, pythonTodo is supposed to be "contained" in pythonComment, which is defined on the line above in the built-in syntax script:

      syn match   pythonComment    "#.*$" contains=pythonTodo,@Spell
      

      As-is, only pythonTodo is listed and simply adding the following to your custom syntax script won't work because it will be overridden by the built-in one:

      syn match pythonComment "#.*$" contains=pythonTodoAlt,pythonTodo,@Spell
      

      nocol

      Bummer.

    2. The solution is to add the containedin attribute to pythonTodoAlt:

      syn keyword pythonTodoAlt WARNING containedin=pythonComment contained
      

      But you are not done yet, because there is no highlighting defined for pythonTodoAlt:

      bare

    3. Syntax scripts tend to define lots and lots of highlight groups but it would be ludicrous to expect every color scheme to handle every highlight group, present and future, explicitly. That's why syntax script authors link their fancy groups, like pythonTodo, to a small set of "default" groups, like Todo. In the built-in syntax script, this is done like so:

      hi def link pythonTodo           Todo
      

      Now, this is the time to decide what to do next.

      One possibility is to link pythonTodoAlt to a different default group, which would be the most "correct" move, but which one? Is there a default highlight group other than Todo that looks the way you want with the current color scheme? Here is an example where I link pythonTodoAlt to Constant because that's the first one that came to mind:

      hi def link pythonTodoAlt Constant
      

      constant

      Meh.

      The other possibility is to directly define the looks of pythonTodoAlt at the color scheme level. If you don't want or can't edit your color scheme, this is how you do it (see this gist):

      " in $MYVIMRC, before defining your color scheme
      augroup MyColors
          autocmd!
          autocmd ColorScheme * highlight pythonTodoAlt cterm=reverse ctermbg=NONE ctermfg=204 gui=reverse guibg=NONE guifg=#ff5f87
      augroup END
      

      done