]> git.eshelyaron.com Git - emacs.git/commitdiff
CC Mode: Optimize scanning of heavily nested brace blocks
authorAlan Mackenzie <acm@muc.de>
Sun, 1 Dec 2024 17:27:34 +0000 (17:27 +0000)
committerEshel Yaron <me@eshelyaron.com>
Wed, 4 Dec 2024 17:02:13 +0000 (18:02 +0100)
This should have fixed bug#74357.

* lisp/progmodes/cc-engine.el (c-laomib-cache): Change the
size of this cache from 4 to 50.
(c-laomib-get-cache, c-laomib-put-cache): Use assq, memq, and
ntake or butlast, rather than looping through the cache with a
cdr loop.  No longer attempt to preserve the "largest" cache
element.
(c-looking-at-or-maybe-in-bracelist): Extend the range covered
by a c-laomib-cache element, rather than adding a second
element to the cache.
(c-no-bracelist-cache): A new cache for the use of
c-inside-bracelist-p, based on the c-parse-state cache.
(c-inside-bracelist-p): Use the new cache to call
c-looking-at-or-maybe-in-bracelist less often.

* lisp/progmodes/cc-mode.el (c-basic-common-init): Initialize
c-no-bracelist-cache.
(c-before-change): Invalidate c-no-bracelist-cache.

(cherry picked from commit b5f1a9e6d3e565585352747733c73d45e6be3bda)

lisp/progmodes/cc-engine.el
lisp/progmodes/cc-mode.el

index 2f130196dd8a77f7c934a56b94645a2f16530c0c..77ee8f9e6e651ed1d7bd7b8f1d5d1f4fc8d82246 100644 (file)
@@ -13045,13 +13045,10 @@ comment at the start of cc-engine.el for more info."
   ;; Return that element or nil if one wasn't found.
   (let ((ptr c-laomib-cache)
        elt)
-    (while
-       (and ptr
-            (setq elt (car ptr))
-            (or (not (eq (car elt) containing-sexp))
-                (< start (car (cddr elt)))))
-      (setq ptr (cdr ptr)))
-    (when ptr
+    (while (and (setq elt (assq containing-sexp ptr))
+               (< start (car (cddr elt))))
+      (setq ptr (cdr (memq elt ptr))))
+    (when elt
       ;; Move the fetched `elt' to the front of the cache.
       (setq c-laomib-cache (delq elt c-laomib-cache))
       (push elt c-laomib-cache)
@@ -13065,46 +13062,24 @@ comment at the start of cc-engine.el for more info."
   (when lim
     (let (old-elt
          (new-elt (list lim start end result))
-         big-ptr
          (cur-ptr c-laomib-cache)
-         togo (size 0) cur-size)
+         size)
 
       ;; If there is an elt which overlaps with the new element, remove it.
-      (while
-         (and cur-ptr
-              (setq old-elt (car cur-ptr))
-              (or (not (eq (car old-elt) lim))
-                  (not (and (> start (car (cddr old-elt)))
-                            (<= start (cadr old-elt))))))
-       (setq cur-ptr (cdr cur-ptr)))
+      (while (and (setq old-elt (assq lim cur-ptr))
+                 (not (and (> start (car (cddr old-elt)))
+                           (<= start (cadr old-elt)))))
+       (setq cur-ptr (cdr (memq old-elt cur-ptr))))
       (when (and cur-ptr old-elt)
        (setq c-laomib-cache (delq old-elt c-laomib-cache)))
 
-      (while (>= (length c-laomib-cache) 4)
-       ;; We delete the least recently used elt which doesn't enclose START,
-       ;; or ...
-       (dolist (elt c-laomib-cache)
-         (if (or (<= start (cadr elt))
-                 (> start (car (cddr elt))))
-             (setq togo elt)))
-
-       ;; ... delete the least recently used elt which isn't the biggest.
-       (when (not togo)
-         (setq cur-ptr c-laomib-cache)
-         (while (cdr cur-ptr)
-           (setq cur-size (- (cadr (cadr cur-ptr))
-                             (car (cddr (cadr cur-ptr)))))
-           (when (> cur-size size)
-             (setq size cur-size
-                   big-ptr cur-ptr))
-           (setq cur-ptr (cdr cur-ptr)))
-         (setq togo (if (cddr big-ptr)
-                        (car (last big-ptr))
-                      (car big-ptr))))
-
-       (setq c-laomib-cache (delq togo c-laomib-cache)))
-
-      (push new-elt c-laomib-cache))))
+      ;; Don't let the cache grow indefinitely.
+      (cond
+       ((fboundp 'ntake)               ; >= Emacs 29.1
+       (setq c-laomib-cache (ntake 49 c-laomib-cache)))
+       ((>= (setq size (length c-laomib-cache)) 50)
+       (setq c-laomib-cache (butlast c-laomib-cache (- size 49)))))
+    (push new-elt c-laomib-cache))))
 
 (defun c-laomib-fix-elt (lwm elt paren-state)
   ;; Correct a c-laomib-cache entry ELT with respect to buffer changes, either
@@ -13151,7 +13126,7 @@ comment at the start of cc-engine.el for more info."
          (setq c-laomib-cache (delq elt c-laomib-cache)))))))
 
 (defun c-looking-at-or-maybe-in-bracelist (&optional containing-sexp lim)
-  ;; Point is at an open brace.  If this starts a brace list, return a list
+  ;; Point is at an open brace.  If this starts a brace list, return a cons
   ;; whose car is the buffer position of the start of the construct which
   ;; introduces the list, and whose cdr is the symbol `in-paren' if the brace
   ;; is directly enclosed in a parenthesis form (i.e. an arglist), t if we
@@ -13290,10 +13265,7 @@ comment at the start of cc-engine.el for more info."
                    (setq sub-bassign-p (c-laomib-loop lim2))
                    (if (<= (point) (cadr cache-entry))
                        (progn
-                         (c-laomib-put-cache containing-sexp
-                                             start (nth 2 cache-entry)
-                                             (nth 3 cache-entry) ;; sub-bassign-p
-                                             )
+                         (setcar (cdr cache-entry) start)
                          (setq braceassignp (nth 3 cache-entry))
                          (goto-char (nth 2 cache-entry)))
                      (c-laomib-put-cache containing-sexp
@@ -13384,14 +13356,20 @@ comment at the start of cc-engine.el for more info."
           (t t))))                     ;; The caller can go up one level.
        ))))
 
+;; A list of the form returned by `c-parse-state'.  Each opening brace in it
+;; is not the brace of a brace list.  Any cons items in it are ignored, and
+;; are also unreliable.
+(defvar c-no-bracelist-cache nil)
+(make-variable-buffer-local 'c-no-bracelist-cache)
+
 (defun c-inside-bracelist-p (containing-sexp paren-state accept-in-paren)
-  ;; return the buffer position of the beginning of the brace list statement
+  ;; Return the buffer position of the beginning of the brace list statement
   ;; if CONTAINING-SEXP is inside a brace list, otherwise return nil.
   ;;
-  ;; CONTAINING-SEXP is the buffer pos of the innermost containing paren.  NO
-  ;; IT ISN'T!!!  [This function is badly designed, and probably needs
-  ;; reformulating without its first argument, and the critical position being
-  ;; at point.]
+  ;; CONTAINING-SEXP must be at an open brace, and is the buffer pos of the
+  ;; innermost containing brace.  NO IT ISN'T!!!  [This function is badly
+  ;; designed, and probably needs reformulating without its first argument,
+  ;; and the critical position being at point.]
   ;;
   ;; PAREN-STATE is the remainder of the state of enclosing braces.
   ;; ACCEPT-IN-PAREN is non-nil iff we will accept as a brace list a brace
@@ -13405,32 +13383,55 @@ comment at the start of cc-engine.el for more info."
   ;; speed.
   ;;
   ;; This function might do hidden buffer changes.
-  ;; this will pick up array/aggregate init lists, even if they are nested.
-   (save-excursion
-     (let ((bufpos t)
-           next-containing)
-       (while (and (eq bufpos t)
-                  containing-sexp)
-        (when paren-state
-          (setq next-containing (c-pull-open-brace paren-state)))
-
-        (goto-char containing-sexp)
-        (if (c-looking-at-inexpr-block next-containing next-containing)
-            ;; We're in an in-expression block of some kind.  Do not
-            ;; check nesting.  We deliberately set the limit to the
-            ;; containing sexp, so that c-looking-at-inexpr-block
-            ;; doesn't check for an identifier before it.
-            (setq bufpos nil)
-          (if (not (eq (char-after) ?{))
-              (setq bufpos nil)
-            (when (eq (setq bufpos (c-looking-at-or-maybe-in-bracelist
-                                         next-containing next-containing))
-                      t)
-              (setq containing-sexp next-containing
-                    next-containing nil)))))
-       (and (consp bufpos)
-           (or accept-in-paren (not (eq (cdr bufpos) 'in-paren)))
-           (car bufpos)))))
+  ;; It will pick up array/aggregate init lists, even if they are nested.
+  (save-excursion
+    (let ((bufpos t)
+         next-containing non-brace-pos
+         (whole-paren-state (cons containing-sexp paren-state))
+         (current-brace containing-sexp))
+      (while (and (eq bufpos t)
+                 current-brace
+                 (not (memq current-brace c-no-bracelist-cache)))
+       (setq next-containing
+             (and paren-state (c-pull-open-brace paren-state)))
+       (goto-char current-brace)
+       (cond
+        ((c-looking-at-inexpr-block next-containing next-containing)
+         ;; We're in an in-expression block of some kind.  Do not
+         ;; check nesting.  We deliberately set the limit to the
+         ;; containing sexp, so that c-looking-at-inexpr-block
+         ;; doesn't check for an identifier before it.
+         (setq bufpos nil))
+        ((not (eq (char-after) ?{))
+         (setq non-brace-pos (point))
+         (setq bufpos nil))
+        ((eq (setq bufpos (c-looking-at-or-maybe-in-bracelist
+                           next-containing next-containing))
+             t)
+         (setq current-brace next-containing))))
+      (cond
+       ((consp bufpos)
+       (and (or accept-in-paren (not (eq (cdr bufpos) 'in-paren)))
+            (car bufpos)))
+       (non-brace-pos
+       ;; We've encountered a ( or a [.  Remove the "middle part" of
+       ;; paren-state, the part that isn't non-brace-list braces, to get the
+       ;; new value of `c-no-bracelist-cache'.
+       (setq whole-paren-state
+             ;; `c-whack-state-before' makes a copy of `whole-paren-state'.
+             (c-whack-state-before (1+ non-brace-pos) whole-paren-state))
+       (while (and next-containing
+                   (not (memq next-containing c-no-bracelist-cache)))
+         (setq next-containing (c-pull-open-brace paren-state)))
+       (setq c-no-bracelist-cache
+             (nconc whole-paren-state
+                    (and next-containing (list next-containing))
+                    paren-state))
+       nil)
+       ((not (memq containing-sexp c-no-bracelist-cache))
+       ;; Update `c-no-bracelist-cache'
+       (setq c-no-bracelist-cache (copy-tree whole-paren-state))
+       nil)))))
 
 (defun c-looking-at-special-brace-list ()
   ;; If we're looking at the start of a pike-style list, i.e., `({ })',
index 058be1c63e3049e88b634156d88cc5c565622130..7f1396df5206efa776f060bd606d59a183dd94cb 100644 (file)
@@ -656,6 +656,8 @@ that requires a literal mode spec at compile time."
 
   ;; Initialize the cache for `c-looking-at-or-maybe-in-bracelist'.
   (setq c-laomib-cache nil)
+  ;; Initialize the cache for non brace-list braces.
+  (setq c-no-bracelist-cache nil)
   ;; Initialize the three literal sub-caches.
   (c-truncate-lit-pos/state-cache 1)
   ;; Initialize the cache of brace pairs, and opening braces/brackets/parens.
@@ -2324,7 +2326,9 @@ with // and /*, not more generic line and block comments."
      ;; The following must happen after the previous, which likely alters
      ;; the macro cache.
      (when c-opt-cpp-symbol
-       (c-invalidate-macro-cache beg end)))))
+       (c-invalidate-macro-cache beg end))
+     (setq c-no-bracelist-cache
+          (c-whack-state-after beg c-no-bracelist-cache)))))
 
 (defvar c-in-after-change-fontification nil)
 (make-variable-buffer-local 'c-in-after-change-fontification)