Subject: Re: bug#3269: 23.0.93; C-mode text highlighting

From: Alan Mackenzie <[email-masked]>
Date: Mon, 18 May 2009 15:06:43 +0000

Hello, Thomas and Yidong!

On Thu, May 14, 2009 at 09:39:24PM +0000, Alan Mackenzie wrote:
> On Wed, May 13, 2009 at 08:39:47AM +0200, Thomas Christensen wrote:

> > In c-mode type:
> > #define FOO "\
> > foo\n\
> > bar\n\
> > "

> > Then place the cursor after foo\n\ and press RETURN for a new line.

> > The highlighting is now broken, and I can only restore it by reverting
> > the buffer.

> That's a bit vague, so let me fill it out for you.  ;-)

> When you press RETURN as described, the new line you've just made lacks a
> backslash, hence terminates the macro.  (Yes, I know you knew that.)

>     #define FOO "\
>     foo\n\

>     bar\n\
>     "

> The "foo" line has lost its fontification, and this is the bug.  I know
> what's causing it, and it _might_ be easily fixable.  What's more, I
> don't this bug was in Emacs 22, so Chong Yidong would allow it to be
> fixed.  :-)

OK, here is a patch for half of the problem - it now fontifies a broken
string in a #define properly - just that you need to type M-o M-o after
the change.  Would you check that this works properly please, Thomas!

May I presume I can commit this to trunk before this week's pretest
release, Yidong?

The second half of the problem is to fix it so that you don't have to
type MoMo afterwards.  Any change you'll let me do this before this
week's pretest, Yidong?

2009-05-18  Alan Mackenzie  <[email-masked]>

	* progmodes/cc-fonts.el (c-font-lock-invalid-cpp-string-matcher):
	New function.
	(c-basic-matchers-before): New clause to fontify invalid strings
	in a CPP construct.

	* progmodes/cc-engine.el: Update some commenting.

	* progmodes/cc-defs.el (c-search-forward-char-property): new
	macro.

*** /home/acm/emacs/emacs.cvs/lisp/progmodes/cc-defs.el	2009-02-12 12:44:40.000000000 +0000
--- cc-defs.el	2009-05-18 10:55:56.716563912 +0000
***************
*** 1029,1034 ****
--- 1029,1053 ----
      ;; Emacs.
      `(remove-text-properties ,from ,to '(,property nil))))
  
+ (defmacro c-search-forward-char-property (property value &optional limit)
+   "Search forward for a text-property PROPERTY having value VALUE.
+ LIMIT bounds the search.  The comparison is done with `equal'.
+ 
+ Leave point just after the character, and set the match data on
+ this character, and return point.  If VALUE isn't found, Return
+ nil; point is then left undefined."
+   `(let ((place (point)))
+      (while
+ 	 (and
+ 	  (< place ,(or limit '(point-max)))
+ 	  (not (equal (get-text-property place ,property) ,value)))
+        (setq place (next-single-property-change
+ 		    place ,property nil ,(or limit '(point-max)))))
+      (when (< place ,(or limit '(point-max)))
+        (goto-char place)
+        (search-forward-regexp ".")	; to set the match-data.
+        (point))))
+ 
  (defun c-clear-char-property-with-value-function (from to property value)
    "Remove all text-properties PROPERTY from the region (FROM, TO)
  which have the value VALUE, as tested by `equal'.  These
*** /home/acm/emacs/emacs.cvs/lisp/progmodes/cc-engine.el	2009-02-21 20:19:30.000000000 +0000
--- cc-engine.el	2009-05-18 10:55:56.779554336 +0000
***************
*** 81,88 ****
  ;;
  ;; 'syntax-table
  ;;   Used to modify the syntax of some characters.  It is used to
! ;;   mark the "<" and ">" of angle bracket parens with paren syntax, and
! ;;   to "hide" obtrusive characters in preprocessor lines.
  ;;
  ;;   This property is used on single characters and is therefore
  ;;   always treated as front and rear nonsticky (or start and end open
--- 81,91 ----
  ;;
  ;; 'syntax-table
  ;;   Used to modify the syntax of some characters.  It is used to
! ;;   mark the "<" and ">" of angle bracket parens with paren syntax;
! ;;   also to "hide" obtrusive characters in preprocessor lines, by
! ;;   marking them with punctuation syntax, '(1).  If this value is ever
! ;;   used for any other purpose, modify
! ;;   `c-font-lock-invalid-cpp-string-matcher' accordingly.
  ;;
  ;;   This property is used on single characters and is therefore
  ;;   always treated as front and rear nonsticky (or start and end open
*** /home/acm/emacs/emacs.cvs/lisp/progmodes/cc-fonts.el	2009-01-05 03:23:18.000000000 +0000
--- cc-fonts.el	2009-05-18 10:55:56.887537920 +0000
***************
*** 285,291 ****
      ;; bit of the overhead compared to a real matcher.  The main reason
      ;; is however to pass the real search limit to the anchored
      ;; matcher(s), since most (if not all) font-lock implementations
!     ;; arbitrarily limits anchored matchers to the same line, and also
      ;; to insulate against various other irritating differences between
      ;; the different (X)Emacs font-lock packages.
      ;;
--- 285,291 ----
      ;; bit of the overhead compared to a real matcher.  The main reason
      ;; is however to pass the real search limit to the anchored
      ;; matcher(s), since most (if not all) font-lock implementations
!     ;; arbitrarily limit anchored matchers to the same line, and also
      ;; to insulate against various other irritating differences between
      ;; the different (X)Emacs font-lock packages.
      ;;
***************
*** 306,312 ****
      ;; covered by the font-lock context.)
  
      ;; Note: Replace `byte-compile' with `eval' to debug the generated
!     ;; lambda easier.
      (byte-compile
       `(lambda (limit)
  	(let (;; The font-lock package in Emacs is known to clobber
--- 306,312 ----
      ;; covered by the font-lock context.)
  
      ;; Note: Replace `byte-compile' with `eval' to debug the generated
!     ;; lambda more easily.
      (byte-compile
       `(lambda (limit)
  	(let (;; The font-lock package in Emacs is known to clobber
***************
*** 559,564 ****
--- 559,583 ----
  	     t)
  	   (c-put-font-lock-face start (1+ start) 'font-lock-warning-face)))))
  
+ (defun c-font-lock-invalid-cpp-string-matcher (lim)
+   ;; Fontify unterminated strings within preprocessor constructs.
+   ;;
+   ;; Unmatched string quotes will have been marked with a punctuation
+   ;; syntax-table text property (value '(1)) by `c-neutralize-syntax-in-CPP'.
+   ;; 
+   ;; This function will be called from font-lock for a region bounded by POINT
+   ;; and LIM, as though it were to identify a keyword for
+   ;; font-lock-keyword-face.  It always returns NIL to inhibit this and
+   ;; prevent a repeat invocation.  See elisp/lispref page "Search-based
+   ;; Fontification".
+   (while (c-search-forward-char-property 'syntax-table '(1) lim) ; punctuation
+     (when (memq (char-before) '(?\" ?\'))
+       (c-put-font-lock-face (1- (point)) (point) 'font-lock-warning-face)
+       (search-forward-regexp "\\(.*\\\\[\n\r]\\)*\\(.*$\\)")
+       (if (> (match-end 0) (match-beginning 0))
+ 	  (c-put-font-lock-face (match-beginning 0) (match-end 0)
+ 				'font-lock-string-face)))))
+ 
  (c-lang-defconst c-basic-matchers-before
    "Font lock matchers for basic keywords, labels, references and various
  other easily recognizable things that should be fontified before generic
***************
*** 580,585 ****
--- 599,611 ----
  	(concat ".\\(" c-string-limit-regexp "\\)")
  	'((c-font-lock-invalid-string)))
  
+       ;; Put a warning face on the opening quote of unclosed strings inside
+       ;; preprocessor construcs (#define).  The previous clause doesn't do
+       ;; this, since the hook function `c-neutralize-syntax-in-CPP' has
+       ;; splatted the syntax of the unmated string quotes.
+       ,@(when (c-lang-const c-opt-cpp-prefix)
+ 	  '((c-font-lock-invalid-cpp-string-matcher)))
+ 
        ;; Fontify keyword constants.
        ,@(when (c-lang-const c-constant-kwds)
  	  (let ((re (c-make-keywords-re nil (c-lang-const c-constant-kwds))))

-- 
Alan Mackenzie (Nuremberg, Germany).