I would like to differentiate syntax color for C define that acts as constant vs macro that is invoked with extra args. So this would trigger it: #define VAR 10 This wouldn't: #define VAR(x) (x * x)

I'm looking for syntax match CConst regex solution that I can put in snytax/c.vim.


  • It is really a matter of whether you can find edge cases or not, but the idea is very simple: :hi def link CConst Special :syntax match CConst "^#define [a-zA-Z]\+\s"
    – r_31415
    Aug 6, 2022 at 23:09
  • @r_31415 sounds like the start of an answer
    – D. Ben Knoble
    Aug 8, 2022 at 14:26

1 Answer 1


Strictly technically speaking, there isn't really a difference between a define that "acts like a constant" vs. a "macro": in both cases the C pre-processor essentially does a simple text replacement.

This somewhat matters because you can do all sorts of weird stuff with macros, to the point of building a kind of DSL.

That said, if we assume that a constant is #define name value and a "macro" is #define name(params) [..], we can modify the default cDefine syntax rule, which looks like this:

syn region cDefine
    \ start="^\s*\zs\%(%:\|#\)\s*\%(define\|undef\)\>"
    \ skip="\\$"
    \ end="$"
    \ keepend contains=ALLBUT,@cPreProcGroup,@Spell


syn region cDefineMacro 
    \ start="^\s*\zs\%(%:\|#\)\s*define\s*\k\{-}("
    \ skip="\\$" end="$" keepend 
    \ contains=ALLBUT,@cPreProcGroup,@Spell

The start is almost identical, except that I removed the undef match (as it's not needed here) and added \s*\k\{-}(. \s* will match whitespace zero or more times, \k matches any keyword character (from the iskeyword setting, \{-} matches that zero or more times non-greedely (*? in Perl regexps), and ( matches a literal (.

I have not tested this super-extensively, but did a bit of spot-checking on the Vim codebase and it seems alright.

You also need to define a :highlight for this, and apply it every time a C file loads, for example in your vimrc:

augroup my-c
    au filetype c syn region cDefineMacro 
                \ start="^\s*\zs\%(%:\|#\)\s*define\s*\k\{-}\s*("
                \ skip="\\$" end="$" keepend 
                \ contains=ALLBUT,@cPreProcGroup,@Spell
                hi def link cDefineMacro Constant
                hi     link cDefine      Macro     " This is already the default
augroup end

This highlights cDefine (constants) as the pre-defined Constant group, and the syntax we added as Macro. You can see a list of predefined groups with just :hi, or you can set your own colouring with ctermfg, ctermbg, etc.

You can also stick it in ~/.vim/after/syntax/c.vim if you prefer (I personally like having everything in my vimrc, but others prefer separate files: your choice). You don't need the autocmd stuff in that case and can just use:

syn [..]
hi [..]
  • Thanks for descriptive answer. Since I wasn't able to make it work precisely as I wanted directly with syntax match, I ended up relying on ctags data. I will definetely try out your solution too. One question though. Does it only highlights constant name where it is #defined or on usage sites too?
    – Vjekoslav
    Aug 15, 2022 at 8:17
  • Getting it to work on usage of CONSTANTS is basically impossible with Vim's highlight system @Vjekoslav, since all it does is apply a bunch of regexps. You need a more advanced script for that. Aug 15, 2022 at 16:53
  • Yeah that's why using tags ended up being better solution.
    – Vjekoslav
    Aug 16, 2022 at 17:12
  • Yeah, but you need to use ctags which not everyone does @Vjekoslav 😅 You should probably post that as an answer if you have the time; it's fine to answer your own questions. Aug 17, 2022 at 2:02

Your Answer

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

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