Todo highlight group currently highlights TODO and FIXME keywords. What do I do to add NOTE, and possibley more keywords, to this existing highlight group, for all file types?

2 Answers 2


Add the following to your .vimrc:

fun! SetMyTodos()
    syn match myTodos /\%(HABA:\)\|\%(NOTE:\)/
    hi link myTodos Todo
autocmd bufenter * :call SetMyTodos()
autocmd filetype * :call SetMyTodos()

Restart. Then everytime you will set filetype (usually when you edit a file) or when you enter a buffer (:e newfile) it will add a myTodos syntax that is highlighted as a Todo.

It is just an example, though :)

enter image description here

  • What's the use of %?
    – 3N4N
    Feb 28, 2019 at 14:34
  • do not create matched groups :h /\%(
    – Maxim Kim
    Feb 28, 2019 at 15:38
  • You can also use syn keyword of course if you need just a words to be highlighted: syn keyword myTodos MYXXX OTHERXXX ANOTHER
    – Maxim Kim
    Feb 28, 2019 at 15:40
  • 1
    Does this highlight the keywords inside comments? I don't think so.
    – Ralf
    Feb 28, 2019 at 22:51
  • It doesn't indeed.
    – Maxim Kim
    Mar 1, 2019 at 4:23

The highlight group Todo just defines how a "todo" should be highlighted. What is actually highlighted by Todo is defined by the syntax. For the C language, the expression is:

syn keyword     cTodo           contained TODO FIXME XXX

This defines the syntax group cTodo, that contains the named keywords. This syntax group is later linked to highlight group Todo with the following:

hi def link cTodo       Todo

If you want to add an additional keyword to the cTodo syntax group, you should create the file ~/.vim/after/syntax/c.vim (~/vimfiles/after/syntax/c.vim on Windows) with the following content:

" Add NOTE as todo keyword
syntax keyword cTodo contained NOTE

So this definition is syntax-specific. For python it would be pythonTodo and the file would be ~.vim/after/syntax/python.vim.

See :help :syn-keyword and :help :syn-contained.

Add-on: How do I delete a keyword from am syntax keyword group?

AFAIK, there is no way to do that directly. You have to clear the entire group and rebuild it. Like:

syntax clear cTodo
syntax keyword cTodo contained TODO FIXME NOTE

Add-on: How do I know how the "todo" syntax group is named for a given language?

Most likely it is <file-type>Todo. To be sure, create a file of this language and add a "todo" comment. Move the cursor to the highlighted word and execute:

echo synIDattr(synID(line("."), col("."), 1), "name")

If you need to do that more than once, you might want to define a new command in Vim by adding the following to your vimrc:

" Show name of syntax element below cursor
command! SynName  echo synIDattr(synID(line("."), col("."), 1), "name")

Update: How to add "todo" keywords for all file types?

This is more complicated. Now you need a autocmd and a function to find the correct syntax groups and extend them.

Why is it important to add the new keywords to the existing definitions? Well, Vim syntax knows contained and contains=.... The "todo" syntax group is defined as contained, that means, that it is only highlighted, when is is contained in another syntax group. On the other hand the syntax group(s) for comments is defined with contains=xxxTodo, so "todos" are highlighted within comments.

Here you go:

function! UpdateTodoKeywords(...)
    let newKeywords = join(a:000, " ")
    let synTodo = map(filter(split(execute("syntax list"), '\n') , { i,v -> match(v, '^\w*Todo\>') == 0}), {i,v -> substitute(v, ' .*$', '', '')})
    for synGrp in synTodo
        execute "syntax keyword " . synGrp . " contained " . newKeywords

augroup now
    autocmd Syntax * call UpdateTodoKeywords("NOTE", "NOTES")
augroup END

The function UpdateTodoKeywords is called whenever a syntax is loaded and adds the given keywords to all "todo" syntax groups. You can add multiple names to the call to UpdateTodoKeywords, as mutch as you want (but at least one).

Note that there are syntax definitions, that contain multiple definitions for "todo". E.g. for Vim script files also python syntax is loaded, so it has vimTodo and pythonTodo.

The function is strait forward, except for the line let synTodo = map(.... Here is what that line does:

  1. It lists all syntax items (execute("syntax list"))
  2. Splits it into a list of lines (split(..., '\n'))
  3. Filters it to only contain lines with syntax group name that ends with Todo (filter(..., { i,v -> match(v, '^\w*Todo\>') == 0}))
  4. Removes everything after syntax group name (map(..., {i,v -> substitute(v, ' .*$', '', '')}))

So the result of this is a list of syntax group names that end with Todo.

Then for every syntax group name it adds the given keywords.

PS: This even works, if the "todo" syntax group is initially defined as syn-match.

Finally: The "brute force attempt":

You could use the following:

autocmd Syntax * syntax keyword myTodo NOTE NOTES containedin=ALL | highlight def link myTodo TODO

This defines a new syntax group myTodo that will be contained in every existing syntax group. So it will highlight "NOTE" and "NOTES" everywhere. In comments, in strings or if you use it as a variable name.

  • What if I want to add NOTE to all of the <filetype>Todo? I mean do I have to add all the filetypes by myself? And if so, then I think it would be simpler to define my own syn keyword, how do I do that (also mention location of the file in this case)?
    – 3N4N
    Feb 28, 2019 at 8:22
  • match, matchadd() wouldn't highlight the pattern for all windows, would it? Then I guess, match is not the answer.
    – 3N4N
    Feb 28, 2019 at 8:51
  • @klaus See my update.
    – Ralf
    Mar 1, 2019 at 8:38
  • Thanks. Your answer is really definitive, I learnt a lot. Sadly I've already selected the correct answer. Gave an upvote though.
    – 3N4N
    Mar 1, 2019 at 18:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.