Skip to content

Instantly share code, notes, and snippets.

@fmoralesc
Last active November 27, 2020 08:58
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fmoralesc/871753b69e665b0b42aa to your computer and use it in GitHub Desktop.
Save fmoralesc/871753b69e665b0b42aa to your computer and use it in GitHub Desktop.

A guide on syn region and conceal.

First, when is it appropiate to use syn region instead of syn match? As a rule of thumb: whenever you would expect the matched text to span several lines (and you don't know how many). If you see yourself using .* in the middle of a syn match rule, you should probably consider using syn region instead.

So let's suppose we want to highlight "{{{begin ... end}}}" blocks, such as this:

{{{begin "But I must explain to you how all this mistaken idea of
denouncing pleasure and praising pain was born and I will give you a
complete account of the system, and expound the actual teachings of the
great explorer of the truth, the master-builder of human happiness. end}}} No one
rejects, dislikes, or avoids pleasure itself, because it is pleasure, but
because those who do not know how to pursue pleasure rationally encounter
consequences that are extremely painful. {{{begin Nor again is there anyone who
loves or pursues or desires to obtain pain of itself, because it is pain,
but because occasionally circumstances occur in which toil and pain can
procure him some great pleasure. end}}} To take a trivial example, which of us
ever undertakes laborious physical exercise, except to obtain some
advantage from it? 

a. A minimal region rule takes a name, a start definition and an end. So we begin from

syn region testRule1 start=/{{{begin\s\=/ end=/\s\=end}}}/

let's highlight this as comments

hi link testRule1 Comment

b. Now, we could want to highlight every instance of pain in the region. We have two options.

  1. We create a rule that matches pain and add it to a contains definition in the testRule1 rule:

    syn region testRule1 start=/{{{begin\s=/ end=/\s=end}}}/ contains=painMatch syn match painMatch /pain/

  2. We add a containedin definition to the painMatch rule:

    syn match painMatch /pain/ containedin=testRule1

    We should probably also add contained to painMatch, so we don't highlight outside regions:

    syn match painMatch /pain/ contained containedin=testRule1

I think 2) is better overall for this case. If you want to highlight things which are available at the toplevel too, you should use 1).

c. To highlight the ends of the region, you can take a couple of approaches.

  1. Give a matchgroup for the ends.

    syn region testRule1 matchgroup=Delimiter start=/{{{begin\s=/ end=/\s=end}}}

  2. If you need to handle both ends differently, you should match them separately in contained matches:

    syn match testEndA /{{{begin/ contained containedin=testRule1 syn match testEndB /end}}}/ contained containedin=testRule1 hi link testEndA Search hi link testEndB Comment

d. To conceal the ends of the region, there's also two approaches you can take, and they correspond, roughly, to the methods we used to highlight the ends:

  1. Give a matchgroup for the ends, and use concealends.

    syn region testRule1 matchgroup=Delimiter start=/{{{begin\s=/ end=/\s=end}}} concealends

Note: concealends only works if you have specified a matchgroup.

  1. To conceal only one of the ends, use conceal in a match for it:

    syn match testEndA /{{{begin/ contained containedin=testRule1 conceal

Optionally, you can specify a character you want conceal to display instead of the end:

syn match testEndA /{{{begin/ contained containedin=testRule1 conceal cchar=+

cchar must be 1 character wide, although unicode can be used if vim's encoding suppports it.

Note: You should not use matchgroup in the region rule in this case. This means you cannot use concealends either.

You can also use cchar with method 1), but this has the issue that the character is shown only at the end of the region. If this is an issue for you, you should use the method 2).

A general caveat when using conceal is that it is not possible to use different colors for different concealed things (the concealed text can take any highlighting, but the concealing text is always highlighted as Conceal.) Compounding this issue, most colorschemes don't support the Conceal group, so the appearance of conceals can be ugly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment