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?

  • . 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 Aug 20 '15 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 Aug 20 '15 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. – Lindydancer Aug 20 '15 at 12:41
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.

  • 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 Aug 20 '15 at 13:56
  • Thanks, this works. I was not aware of the *? to match as little as possible. – laifs Aug 21 '15 at 0:34
3

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

(\*\(?:[^*]\|\*+[^)*]\)*\*+)
  • Just have to double up the \ to match his request (I'm assuming for use in elisp not just as a re-search). – Jonathan Leech-Pepin Aug 20 '15 at 15:26
  • That goes along with adding double quotes around it, yes. – Stefan Aug 20 '15 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 Aug 21 '15 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 ")". – Andreas Röhler Aug 21 '15 at 6:14

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.