3

This problem that seems trivial is beating me real hard.

I'm trying to create a couple of syntax highlighting directives for a custom language that is structured in paragraphs separated by at least one empty line. The language specifies that paragraphs can eiter:

  1. End with punctuation
  2. End without punctuation

Each of these cases must have different highlighting properties. On the images below I have one example structure demonstrating what I need to achieve:

Case 1: Paragraphs that ends with some punctuation comprises one highlighting group:

Case 1: Paragraphs that ends with punctuation

Case 2: Paragraphs that ends without punctuation comprises another highlighting group:

Case 2: Paragraphs that ends without punctuation

I got no luck with syn region, since there are no constant start/end characters surrounding the blocks of text, so I'm trying to solve this with syn match and thus I'm exploring the possibilities with complete regexes.

First I've verified that \n$ correctly maps to the end of every paragraph, and that ^$\n correctly maps the empty lines, so I composed them as follows:

^$\n\_.\{-}\n$

Which matches all paragraphs except the first one. So I implemented:

\(\%^\|^$\n\)\_.\{-}\n$

Which correctly matches all paragraphs, but when I tried to account for the punctuation:

\(\%^\|^$\n\)\_.\{-}\.\n$

It became clear that the \_. operator, even with the non-greedy multi \{-} just ignores the \. bit on the rule, and will match every paragraph from the start of the file, even the ones that do not end with a ., up to the last paragraph on the file that ends with a . . The weird thing to me is that even that it matches every paragraph on the file up to the last one ending with a ., the match returns a distinct result count (more than one result). In this case I would expect a single match. What is happening here?

So, I've read all the manuals and I'm clueless regarding what strategy to use from here.

Any help will be greatly appreciated. Thank you!

1

Here's what I came up with:

Solution 1

To match the paragraphs ending with a dot:

\(\%^\|^\n\)\zs\(.\+\n\)*.\+[.]\ze\(\n\n\|\n*\%$\)

To match the paragraphs ending with no dot:

\(\%^\|^\n\)\zs\(.\+\n\)*.\+[^.]\ze\(\n\n\|\n*\%$\)
#                            ^
#                      This is added

Or with the very magic flag:

\v(%^|^\n)\zs(.+\n)*.+[.]\ze(\n\n|\n*%$)
# AND
\v(%^|^\n)\zs(.+\n)*.+[^.]\ze(\n\n|\n*%$)

Explanations

  • (%^|^\n) marks the beggining of a paragraph, might be the begging of the file as well so we add %^
  • (.+\n) is one line, it can contain anything as long as it's not empty
  • * We want our paragraph to have as many line as it wants
  • .+[.] is the last line, which has to end with a dot
  • (\n\n|\n*%$) Is the end of the paragraph, which might also be the endo of the file (%$)

Solution 2

Here's another solution that looks a bit more like your initial idea:

\(^\n\|\%^\)\zs\(\_.\(\n\n\)\@!\)*[.]\ze\(\n$\|\%$\)
\(^\n\|\%^\)\zs\(\_.\(\n\n\)\@!\)*[^.]\ze\(\n$\|\%$\)

# With the Very special flag:

\v(^\n|%^)\zs(\_.(\n\n)@!)*[.]\ze(\n$|%$)
\v(^\n|%^)\zs(\_.(\n\n)@!)*[^.]\ze(\n$|%$)

Explanations:

  • (^\n|%^) to start the paragraph
  • (\_.(\n\n)@!) is any character (including newline) which is not followed by 2 newlines. in other words, any character which is not the end of a paragraph. (It's a negative look ahead, :help \@!)
  • * We want that character as many times as possible
  • [.] is the last character of the paragraph (could be replaced by \.)
  • (\n$|%$) to end the paragraph
| improve this answer | |
  • I have a quick question, not so related. How do these weird symbols work, and where I can learn them? – Mahbub Alam Apr 8 at 7:56
  • 1
    Hi @MahbubAlam, Those "weird symbols" form a regular expression, also known as Regex or Regexp. If you're only interested in vim regex, you can access the documentation by typing :help pattern-searches in vim. If you are interested in regex in general (because it's present in pretty much any programming language), you can just google it, and I'm sure you'll find what you're looking for :) – Zorzi Apr 8 at 11:35
  • Got it. Both \zs and \ze were very new concepts to me, never realized that they could be so expressive in this context. Thank you. – Jon Mod Apr 8 at 22:01
  • Weird thing: The rule works on a regular search (/), but only match the first line on the file when used inside a syn match. Had to remove the \zs bit. Go figure… – Jon Mod Apr 8 at 22:23
  • 1
    Oh yes, I forgot that you were working with syntax regex here, my bad! You might be interested in looking into :h syn-pattern-offset to mimic the behavior of \zs and \ze – Zorzi Apr 9 at 13:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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