5

I'm writing a major mode for a language that uses (* ... *) as comments.

I'm trying to match multi-line comments. e.g.

(*
foo
bar
*)

I was initially doing "(\\*.*\\*)", however, . doesn't match newline.

I then tried what the wiki on multi-line regex said: "(\\*[\0-\377[:nonascii:]]*\\*)". However, this leads to matching that I wasn't expecting. For instance, if I have the following:

(* comment 1*)
code block 1

(* comment 2*)
code block 2

(* comment 3*)
code block 3

It highlights everything from comment 1 to comment 3 as a big comment, including code block 1 and code block 2.

How can I get it to match (* to the first closing *) rather than the last one?

3
  • . matches any character but a newline, you have to to use (.|\n). This however is inefficient when there are several newlines. Better match multiple lines with (.*\n?)* instead. The newline is optional so that the expression can end in the middle of a line.
    – mutbuerger
    Commented Aug 20, 2015 at 7:36
  • I think [\0-\377[:nonascii:]] is better than (.*\n?)*, which is so slow it crashes emacs on my computer. However, my problem isn't matching newline, but matching (* with the first *). As I explain in the question, it matches the last *), which is not good. I have tried matching everything but (* in the blocks, but it doesn't seem to match things character by character, rather, it matches the whole thing.
    – laifs
    Commented Aug 20, 2015 at 10:59
  • 2
    Typically, when you are matching comment using regexp:s, you are using the wrong tool. Instead, make sure your major mode set up the syntax table correctly and use functions like syntax-ppss to check if you are in a comment, forward-comment to move over comments etc. Commented Aug 20, 2015 at 12:41

2 Answers 2

7

You will have to change the wildcard match to a non-greedy version (+?,??,*?). Ref: Emacs Manual: Regexp

In this case:

(\\*[\0-\377[:nonascii:]]*?\\*)

That tells it to find the shortest full match, rather than the longest.

2
  • Note that "(\\*[\0-\377[:nonascii:]]*?\\*)toto" will still match the whole (*foo*)titi(*bar*)toto, so in some cases the non-greedy solution is not sufficient.
    – Stefan
    Commented Aug 20, 2015 at 13:56
  • Thanks, this works. I was not aware of the *? to match as little as possible.
    – laifs
    Commented Aug 21, 2015 at 0:34
3

See Jonathan's answer for the easy solution. The hard solution would look something like:

(\*\(?:[^*]\|\*+[^)*]\)*\*+)
4
  • Just have to double up the \ to match his request (I'm assuming for use in elisp not just as a re-search). Commented Aug 20, 2015 at 15:26
  • That goes along with adding double quotes around it, yes.
    – Stefan
    Commented Aug 20, 2015 at 19:56
  • (\\*\\(?:[^*]\\|\\*+[^)*]\\)*\\*+) works too. I was also thinking of doing it this way (i.e. matching (* but not matching any (* ... *) in between), but it seemed awkward, like something the regexp should handle instead. *? does the job.
    – laifs
    Commented Aug 21, 2015 at 0:40
  • Don't think [^*]\|*+[^)*] makes sense here. Both parts are not sufficient, resp. not required. Would need a look-ahead assertion WRT "*" followed by a ")". Commented Aug 21, 2015 at 6:14

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.