This question might be related to Custom syntax highlighting: highlight word following keyword but after studying that for hours, I could not get it to work.

What I want is to highlight vulkan types that match vk::(Instance|Device) (in reality a much longer expression, but this will do as example).

It should not match vk::InstanceCreateInfo (because that is a different type). It should work when this match is contained inside a unrelated region. For example, I have a very complex regions that match parenthesis for different type of things; so complex that I really don't want this to be added as exception too. Yet, the highlight should work in code like:

void setup(vk::Instance vh_instance, vk::SurfaceKHR surface, DeviceCreateInfo&& device_create_info);

Aka, work while contained inside region cParen.

I already have:

syn keyword cwNamespaceTag vk

which is auto-generated; I'd prefer to be able to leave that alone. But if necessary it can be changed.

I tried the following (already changing the above keyword):

syn keyword cwNamespaceVkTag vk nextgroup=cwVulkanHandle
syn match cwVulkanHandle '\v::(Instance|Device)>' contained

but that fails in that ::Device in

void setup(foo::Device foo);

is highlighted (because it is contained (in cParen). Obviously this is not what I want; cwVulkanHandle should only be enabled when immediately following cwNamespaceVkTag.

I also tried,

syn keyword cwNamespaceVkTag vk contained
syn match cwVulkanHandle '\v<vk::(Instance|Device)>' contains=cwNamespaceVkTag

but that highlights vk (as cwNamespaceVkTag) and then cwVulkanHandle isn't used anymore for some reason. When I remove the keyword line then the match works. It seems that the fact that vk already matches a keyword stops the region from matching against it, despite that I say that it contains it.

How can I get this to work?



syn match cwNamespaceVkTag /vk::\(Instance\|Device\)\>/ contains=cwVulkanHandle
syn match cwVulkanHandle /::\w\+/ contained

leads to

enter image description here

2 Answers 2


How about this?

syn region cParen start=/setup(/ end=/)/ contains=cwNamespaceVkTag
syn match cwNamespaceVkTag /\<vk::\(Instance\|Device\)\>/ contains=cwVulkanHandle contained
syn match cwVulkanHandle /::\w\+/ contained

I didn't use the \v (very magic) atom because I'm more used to Vim's "standard" regular expression format.

  • Sorry for the late reply, I was sleeping. Thank you for your help. I tried it and added a picture of the result to my answer (see below 'EDIT'). The main problem is still that when contained inside cParen the match rule for cwVulkanHandle still matches.
    – Carlo Wood
    Commented Aug 22, 2021 at 10:04
  • 2
    Try the containedin to specify where, and a word boundary at the front to prevent spurious matches like vvk
    – D. Ben Knoble
    Commented Aug 22, 2021 at 11:05
  • 1
    @CarloWood: I've edited my answer, see if it helps?
    – Heptite
    Commented Aug 22, 2021 at 15:45
  • Now it highlights every ::\w\+.
    – Carlo Wood
    Commented Aug 22, 2021 at 16:21
  • I forgot that containedin still means contained is needed. I edited again.
    – Heptite
    Commented Aug 22, 2021 at 17:48

I moved on, after hacking something together that works for now:

" Add syntax group for vulkan types that are handles.

" The regular expression was generated with:
" RE=$(egrep '^(VK_DEFINE_NON_DISPATCHABLE_HANDLE|VK_DEFINE_HANDLE)' Vulkan-Hpp/Vulkan-Headers/include/vulkan/vulkan_core.h | sed -e 's/.*(\([^)]*\)).*/\1/;s/^Vk//' | sort | awk '{ printf("%s|", $0); }' | sed -e 's/^/(/;s/|$/)/')
syn match cwNameSpaceTag "\v<vk>"
syn match cwVulkanHandle "\v<vk::(AccelerationStructureKHR|AccelerationStructureNV|Buffer|BufferView|CommandBuffer|CommandPool|CuFunctionNVX|CuModuleNVX|DebugReportCallbackEXT|DebugUtilsMessengerEXT|DeferredOperationKHR|DescriptorPool|DescriptorSet|DescriptorSetLayout|DescriptorUpdateTemplate|Device|DeviceMemory|DisplayKHR|DisplayModeKHR|Event|Fence|Framebuffer|Image|ImageView|IndirectCommandsLayoutNV|Instance|PerformanceConfigurationINTEL|PhysicalDevice|Pipeline|PipelineCache|PipelineLayout|PrivateDataSlotEXT|QueryPool|Queue|RenderPass|Sampler|SamplerYcbcrConversion|Semaphore|ShaderModule|SurfaceKHR|SwapchainKHR|ValidationCacheEXT)>"hs=s+4 contains=cwNamespaceTag
syn match cwVulkanHandle "\v<Vk(AccelerationStructureKHR|AccelerationStructureNV|Buffer|BufferView|CommandBuffer|CommandPool|CuFunctionNVX|CuModuleNVX|DebugReportCallbackEXT|DebugUtilsMessengerEXT|DeferredOperationKHR|DescriptorPool|DescriptorSet|DescriptorSetLayout|DescriptorUpdateTemplate|Device|DeviceMemory|DisplayKHR|DisplayModeKHR|Event|Fence|Framebuffer|Image|ImageView|IndirectCommandsLayoutNV|Instance|PerformanceConfigurationINTEL|PhysicalDevice|Pipeline|PipelineCache|PipelineLayout|PrivateDataSlotEXT|QueryPool|Queue|RenderPass|Sampler|SamplerYcbcrConversion|Semaphore|ShaderModule|SurfaceKHR|SwapchainKHR|ValidationCacheEXT)>"

hi def link cwVulkanHandle cVulkanHandle                    " Highlight vulkan handle types.

For this to work I also had to make sure that the previous keyword rule for vk was no longer generated.

Or as short version that we used here as example / test case:

syn match cwNameSpaceTag "\v<vk>"
syn match cwVulkanHandle "\v<vk::(Instance|Device)>"hs=s+4 contains=cwNamespaceTag

The main trick here is using a match for the namespace vk instead of a keyword. Apparently a keyword always has a higher precedence, so when that exists you can not add a match that contains it. By using another match it works: when the match that follow the first one matches, it become the one that is picked, overruling the first more general match.

My conclusion is that vim's highlighting interface needs a rewrite: for most things that are just a little bit more advanced one quickly has to resort to hacks and far from intuitive. I suppose that the preferred method (for C++) would be one that is based of llvm (clang).

Things that I ran into:

  1. It is not possible to delete/remove a rule later on (ie, a keyword rule).
  2. It is not possible to explicitly state that rule should ONLY match when contained in LIST, automatically avoiding any existing rules with a contains=ALL_BUT,... without having to add to each and every such list.
  3. I could not figure out how (seems not possible) to match a start pattern against text that was already matched (vk::), which made it impossible to even begin to use regions here.

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.