Subject: bug#40052: CC Mode 5.34 (C++//l); cc-mode hangs fontifying src/hotspot/share/runtime/globals.hpp in OpenJDK

From: Alan Mackenzie <[email-masked]>
Date: Sat, 18 Apr 2020 09:39:36 +0000

Hello, Nick.

On Fri, Apr 17, 2020 at 13:15:49 +0800, Nick Gasson wrote:

> > In my timings, scrolling with the patch applied is about four times as
> > fast as without it.  I've also included a correction to a bug about
> > "regexp stack overflow" which happened when one tried actually to edit
> > this buffer.  Clearly this speed is still not fast enough, but it might
> > be bearable at a pinch.  I will carry on trying to speed it up.

> I applied this patch and it makes a huge difference. It's still a little
> slow as you say but it's usable now. Thanks a lot!

OK, thanks.  I've had another look into CC Mode, and found a bug in the
handling of its macro cache.  It isn't saving the position of the end of
the macro, hence is doing too much scanning.

I've fixed this in the following patch, which gives another factor of ~5
speedup in the scrolling.  Please remove the previous patch before
applying this new one.

However, the scrolling still gets sluggish towards the end of
globals.hpp.  This is because CC Mode's function which goes back to the
beginning of a statement is scanning back over the entire macro.  It
basically looks for semicolons or closing braces (at top level) and
there are none in the huge macro.  I tried creating a variant of
globals.hpp, where each "function call" was terminated by a semicolon.
The sluggishness was entirely fixed.  This is largely theoretical, as I
assume you have no control over the contents of globals.hpp.

Anyhow, here is the patch.  Please let me know if there are still
problems with it.  Thanks!

diff -r 99da14f7dde7 cc-engine.el
--- a/cc-engine.el	Sun Mar 08 15:07:06 2020 +0000
+++ b/cc-engine.el	Sat Apr 18 09:04:42 2020 +0000
@@ -412,7 +412,7 @@
 	  (when (and (car c-macro-cache)
 		     (> (point) (car c-macro-cache)) ; in case we have a
 					; zero-sized region.
-		     (not (eq (char-before (1- (point))) ?\\)))
+		     (not lim))
 	    (setcdr c-macro-cache (point))
 	    (setq c-macro-cache-syntactic nil)))))))
 
@@ -1648,6 +1648,21 @@
 	    (forward-char 2)
 	    t))))
 
+(defmacro c-forward-comment-minus-1 ()
+  "Call (forward-comment -1), taking care of escaped newlines.
+Return the result of `forward-comment' if it gets called, nil otherwise."
+  `(if (not comment-end-can-be-escaped)
+       (forward-comment -1)
+     (when (and (< (skip-syntax-backward " >") 0)
+		(eq (char-after) ?\n))
+       (forward-char))
+     (cond
+      ((and (eq (char-before) ?\n)
+	    (eq (char-before (1- (point))) ?\\))
+       (backward-char)
+       nil)
+      (t (forward-comment -1)))))
+
 (defun c-backward-single-comment ()
   "Move backward past whitespace and the closest preceding comment, if any.
 Return t if a comment was found, nil otherwise.  In either case, the
@@ -1681,12 +1696,12 @@
       ;; same line.
       (re-search-forward "\\=\\s *[\n\r]" start t)
 
-      (if (if (forward-comment -1)
+      (if (if (c-forward-comment-minus-1)
 	      (if (eolp)
 		  ;; If forward-comment above succeeded and we're at eol
 		  ;; then the newline we moved over above didn't end a
 		  ;; line comment, so we give it another go.
-		  (forward-comment -1)
+		  (c-forward-comment-minus-1)
 		t))
 
 	  ;; Emacs <= 20 and XEmacs move back over the closer of a
@@ -1715,7 +1730,7 @@
 
 	    (if (let (moved-comment)
 		  (while
-		      (and (not (setq moved-comment (forward-comment -1)))
+		      (and (not (setq moved-comment (c-forward-comment-minus-1)))
 		      ;; Cope specifically with ^M^J here -
 		      ;; forward-comment sometimes gets stuck after ^Ms,
 		      ;; sometimes after ^M^J.
diff -r 99da14f7dde7 cc-mode.el
--- a/cc-mode.el	Sun Mar 08 15:07:06 2020 +0000
+++ b/cc-mode.el	Sat Apr 18 09:04:42 2020 +0000
@@ -1413,7 +1413,7 @@
 
       ;; Move to end of logical line (as it will be after the change, or as it
       ;; was before unescaping a NL.)
-      (re-search-forward "\\(\\\\\\(.\\|\n\\)\\|[^\\\n\r]\\)*" nil t)
+      (re-search-forward "\\(?:\\\\\\(?:.\\|\n\\)\\|[^\\\n\r]\\)*" nil t)
       ;; We're at an EOLL or point-max.
       (if (equal (c-get-char-property (point) 'syntax-table) '(15))
 	  (if (memq (char-after) '(?\n ?\r))
@@ -1521,7 +1521,7 @@
 	   (progn
 	     (goto-char (min (1+ end)	; 1+, in case a NL has become escaped.
 			     (point-max)))
-	     (re-search-forward "\\(\\\\\\(.\\|\n\\)\\|[^\\\n\r]\\)*"
+	     (re-search-forward "\\(?:\\\\\\(?:.\\|\n\\)\\|[^\\\n\r]\\)*"
 				nil t)
 	     (point))
 	   c-new-END))
@@ -1602,8 +1602,8 @@
 		   (c-beginning-of-macro))))
       (goto-char (1+ end))		; After the \
       ;; Search forward for EOLL
-      (setq lim (re-search-forward "\\(\\\\\\(.\\|\n\\)\\|[^\\\n\r]\\)*"
-				   nil t))
+      (setq lim (re-search-forward "\\(?:\\\\\\(?:.\\|\n\\)\\|[^\\\n\r]\\)*"
+       				   nil t))
       (goto-char (1+ end))
       (when (c-search-forward-char-property-with-value-on-char
 	     'syntax-table '(15) ?\" lim)

> Nick

-- 
Alan Mackenzie (Nuremberg, Germany).