summaryrefslogtreecommitdiff
path: root/lisp/progmodes/cc-engine.el
diff options
context:
space:
mode:
authorAlan Mackenzie <acm@muc.de>2012-03-02 22:16:21 +0000
committerAlan Mackenzie <acm@muc.de>2012-03-02 22:16:21 +0000
commit56d093a9c8cc3628b028c695c11815f6bb190d3f (patch)
treec895058cf037d7836fd9ab5f68d2c85bb47f6a22 /lisp/progmodes/cc-engine.el
parent3e441275a89ba751d227b514981ccb413034b3bb (diff)
downloademacs-56d093a9c8cc3628b028c695c11815f6bb190d3f.tar.gz
Depessimize the handling of very large macros.
Diffstat (limited to 'lisp/progmodes/cc-engine.el')
-rw-r--r--lisp/progmodes/cc-engine.el233
1 files changed, 178 insertions, 55 deletions
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index 95b43e763d5..3b33ac894f2 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -219,6 +219,38 @@
(point))))
c-macro-start))
+;; One element macro cache to cope with continual movement within very large
+;; CPP macros.
+(defvar c-macro-cache nil)
+(make-variable-buffer-local 'c-macro-cache)
+;; Nil or cons of the bounds of the most recent CPP form probed by
+;; `c-beginning-of-macro', `c-end-of-macro' or `c-syntactic-end-of-macro'.
+;; The cdr will be nil if we know only the start of the CPP form.
+(defvar c-macro-cache-start-pos nil)
+(make-variable-buffer-local 'c-macro-cache-start-pos)
+;; The starting position from where we determined `c-macro-cache'.
+(defvar c-macro-cache-syntactic nil)
+(make-variable-buffer-local 'c-macro-cache-syntactic)
+;; non-nil iff `c-macro-cache' has both elements set AND the cdr is at a
+;; syntactic end of macro, not merely an apparent one.
+
+(defun c-invalidate-macro-cache (beg end)
+ ;; Called from a before-change function. If the change region is before or
+ ;; in the macro characterised by `c-macro-cache' etc., nullify it
+ ;; appropriately. BEG and END are the standard before-change-functions
+ ;; parameters. END isn't used.
+ (cond
+ ((null c-macro-cache))
+ ((< beg (car c-macro-cache))
+ (setq c-macro-cache nil
+ c-macro-cache-start-pos nil
+ c-macro-cache-syntactic nil))
+ ((and (cdr c-macro-cache)
+ (< beg (cdr c-macro-cache)))
+ (setcdr c-macro-cache nil)
+ (setq c-macro-cache-start-pos beg
+ c-macro-cache-syntactic nil))))
+
(defun c-beginning-of-macro (&optional lim)
"Go to the beginning of a preprocessor directive.
Leave point at the beginning of the directive and return t if in one,
@@ -226,19 +258,36 @@ otherwise return nil and leave point unchanged.
Note that this function might do hidden buffer changes. See the
comment at the start of cc-engine.el for more info."
- (when c-opt-cpp-prefix
- (let ((here (point)))
- (save-restriction
- (if lim (narrow-to-region lim (point-max)))
- (beginning-of-line)
- (while (eq (char-before (1- (point))) ?\\)
- (forward-line -1))
- (back-to-indentation)
- (if (and (<= (point) here)
- (looking-at c-opt-cpp-start))
- t
- (goto-char here)
- nil)))))
+ (let ((here (point)))
+ (when c-opt-cpp-prefix
+ (if (and (car c-macro-cache)
+ (>= (point) (car c-macro-cache))
+ (or (and (cdr c-macro-cache)
+ (<= (point) (cdr c-macro-cache)))
+ (<= (point) c-macro-cache-start-pos)))
+ (unless (< (car c-macro-cache) (or lim (point-min)))
+ (progn (goto-char (max (or lim (point-min)) (car c-macro-cache)))
+ (setq c-macro-cache-start-pos
+ (max c-macro-cache-start-pos here))
+ t))
+ (setq c-macro-cache nil
+ c-macro-cache-start-pos nil
+ c-macro-cache-syntactic nil)
+
+ (save-restriction
+ (if lim (narrow-to-region lim (point-max)))
+ (beginning-of-line)
+ (while (eq (char-before (1- (point))) ?\\)
+ (forward-line -1))
+ (back-to-indentation)
+ (if (and (<= (point) here)
+ (looking-at c-opt-cpp-start))
+ (progn
+ (setq c-macro-cache (cons (point) nil)
+ c-macro-cache-start-pos here)
+ t)
+ (goto-char here)
+ nil))))))
(defun c-end-of-macro ()
"Go to the end of a preprocessor directive.
@@ -248,12 +297,24 @@ done that the point is inside a cpp directive to begin with.
Note that this function might do hidden buffer changes. See the
comment at the start of cc-engine.el for more info."
- (while (progn
- (end-of-line)
- (when (and (eq (char-before) ?\\)
- (not (eobp)))
- (forward-char)
- t))))
+ (if (and (cdr c-macro-cache)
+ (<= (point) (cdr c-macro-cache))
+ (>= (point) (car c-macro-cache)))
+ (goto-char (cdr c-macro-cache))
+ (unless (and (car c-macro-cache)
+ (<= (point) c-macro-cache-start-pos)
+ (>= (point) (car c-macro-cache)))
+ (setq c-macro-cache nil
+ c-macro-cache-start-pos nil
+ c-macro-cache-syntactic nil))
+ (while (progn
+ (end-of-line)
+ (when (and (eq (char-before) ?\\)
+ (not (eobp)))
+ (forward-char)
+ t)))
+ (when (car c-macro-cache)
+ (setcdr c-macro-cache (point)))))
(defun c-syntactic-end-of-macro ()
;; Go to the end of a CPP directive, or a "safe" pos just before.
@@ -268,12 +329,15 @@ comment at the start of cc-engine.el for more info."
;; at the start of cc-engine.el for more info.
(let* ((here (point))
(there (progn (c-end-of-macro) (point)))
- (s (parse-partial-sexp here there)))
- (while (and (or (nth 3 s) ; in a string
- (nth 4 s)) ; in a comment (maybe at end of line comment)
- (> there here)) ; No infinite loops, please.
- (setq there (1- (nth 8 s)))
- (setq s (parse-partial-sexp here there)))
+ s)
+ (unless c-macro-cache-syntactic
+ (setq s (parse-partial-sexp here there))
+ (while (and (or (nth 3 s) ; in a string
+ (nth 4 s)) ; in a comment (maybe at end of line comment)
+ (> there here)) ; No infinite loops, please.
+ (setq there (1- (nth 8 s)))
+ (setq s (parse-partial-sexp here there)))
+ (setq c-macro-cache-syntactic (car c-macro-cache)))
(point)))
(defun c-forward-over-cpp-define-id ()
@@ -2089,6 +2153,18 @@ comment at the start of cc-engine.el for more info."
;; reduced by buffer changes, and increased by invocations of
;; `c-state-literal-at'.
+(defvar c-state-semi-nonlit-pos-cache nil)
+(make-variable-buffer-local 'c-state-semi-nonlit-pos-cache)
+;; A list of buffer positions which are known not to be in a literal. This is
+;; ordered with higher positions at the front of the list. Only those which
+;; are less than `c-state-semi-nonlit-pos-cache-limit' are valid.
+
+(defvar c-state-semi-nonlit-pos-cache-limit 1)
+(make-variable-buffer-local 'c-state-semi-nonlit-pos-cache-limit)
+;; An upper limit on valid entries in `c-state-semi-nonlit-pos-cache'. This is
+;; reduced by buffer changes, and increased by invocations of
+;; `c-state-literal-at'. FIMXE!!!
+
(defsubst c-state-pp-to-literal (from to)
;; Do a parse-partial-sexp from FROM to TO, returning either
;; (STATE TYPE (BEG . END)) if TO is in a literal; or
@@ -2129,48 +2205,93 @@ comment at the start of cc-engine.el for more info."
(widen)
(save-excursion
(let ((c c-state-nonlit-pos-cache)
- pos npos lit macro-beg macro-end)
+ pos npos high-pos lit macro-beg macro-end)
;; Trim the cache to take account of buffer changes.
(while (and c (> (car c) c-state-nonlit-pos-cache-limit))
(setq c (cdr c)))
(setq c-state-nonlit-pos-cache c)
(while (and c (> (car c) here))
+ (setq high-pos (car c))
(setq c (cdr c)))
(setq pos (or (car c) (point-min)))
- (while
- ;; Add an element to `c-state-nonlit-pos-cache' each iteration.
- (and
- (<= (setq npos (+ pos c-state-nonlit-pos-interval)) here)
+ (unless high-pos
+ (while
+ ;; Add an element to `c-state-nonlit-pos-cache' each iteration.
+ (and
+ (<= (setq npos (+ pos c-state-nonlit-pos-interval)) here)
- ;; Test for being in a literal.
- (progn
- (setq lit (car (cddr (c-state-pp-to-literal pos npos))))
- (or (null lit)
- (prog1 (<= (cdr lit) here)
- (setq npos (cdr lit)))))
+ ;; Test for being in a literal. If so, go to after it.
+ (progn
+ (setq lit (car (cddr (c-state-pp-to-literal pos npos))))
+ (or (null lit)
+ (prog1 (<= (cdr lit) here)
+ (setq npos (cdr lit)))))
- ;; Test for being in a macro.
- (progn
- (goto-char npos)
- (setq macro-beg
- (and (c-beginning-of-macro) (/= (point) npos) (point)))
- (when macro-beg
- (c-syntactic-end-of-macro)
- (or (eobp) (forward-char))
- (setq macro-end (point)))
- (or (null macro-beg)
- (prog1 (<= macro-end here)
- (setq npos macro-end)))))
-
- (setq pos npos)
- (setq c-state-nonlit-pos-cache (cons pos c-state-nonlit-pos-cache)))
+ ;; Test for being in a macro. If so, go to after it.
+ (progn
+ (goto-char npos)
+ (setq macro-beg
+ (and (c-beginning-of-macro) (/= (point) npos) (point)))
+ (when macro-beg
+ (c-syntactic-end-of-macro)
+ (or (eobp) (forward-char))
+ (setq macro-end (point)))
+ (or (null macro-beg)
+ (prog1 (<= macro-end here)
+ (setq npos macro-end)))))
+
+ (setq pos npos)
+ (setq c-state-nonlit-pos-cache (cons pos c-state-nonlit-pos-cache)))
+ ;; Add one extra element above HERE so as to to avoid the previous
+ ;; expensive calculation when the next call is close to the current
+ ;; one. This is especially useful when inside a large macro.
+ (setq c-state-nonlit-pos-cache (cons npos c-state-nonlit-pos-cache)))
(if (> pos c-state-nonlit-pos-cache-limit)
(setq c-state-nonlit-pos-cache-limit pos))
pos))))
+(defun c-state-semi-safe-place (here)
+ ;; Return a buffer position before HERE which is "safe", i.e. outside any
+ ;; string or comment. It may be in a macro.
+ (save-restriction
+ (widen)
+ (save-excursion
+ (let ((c c-state-semi-nonlit-pos-cache)
+ pos npos high-pos lit macro-beg macro-end)
+ ;; Trim the cache to take account of buffer changes.
+ (while (and c (> (car c) c-state-semi-nonlit-pos-cache-limit))
+ (setq c (cdr c)))
+ (setq c-state-semi-nonlit-pos-cache c)
+
+ (while (and c (> (car c) here))
+ (setq high-pos (car c))
+ (setq c (cdr c)))
+ (setq pos (or (car c) (point-min)))
+
+ (unless high-pos
+ (while
+ ;; Add an element to `c-state-semi-nonlit-pos-cache' each iteration.
+ (and
+ (<= (setq npos (+ pos c-state-nonlit-pos-interval)) here)
+
+ ;; Test for being in a literal. If so, go to after it.
+ (progn
+ (setq lit (car (cddr (c-state-pp-to-literal pos npos))))
+ (or (null lit)
+ (prog1 (<= (cdr lit) here)
+ (setq npos (cdr lit))))))
+
+ (setq pos npos)
+ (setq c-state-semi-nonlit-pos-cache
+ (cons pos c-state-semi-nonlit-pos-cache))))
+
+ (if (> pos c-state-semi-nonlit-pos-cache-limit)
+ (setq c-state-semi-nonlit-pos-cache-limit pos))
+ pos))))
+
(defun c-state-literal-at (here)
;; If position HERE is inside a literal, return (START . END), the
;; boundaries of the literal (which may be outside the accessible bit of the
@@ -2985,9 +3106,11 @@ comment at the start of cc-engine.el for more info."
;;
;; This function is called from c-after-change.
- ;; The cache of non-literals:
+ ;; The caches of non-literals:
(if (< here c-state-nonlit-pos-cache-limit)
(setq c-state-nonlit-pos-cache-limit here))
+ (if (< here c-state-semi-nonlit-pos-cache-limit)
+ (setq c-state-semi-nonlit-pos-cache-limit here))
;; `c-state-cache':
;; Case 1: if `here' is in a literal containing point-min, everything
@@ -4230,7 +4353,7 @@ Note that this function might do hidden buffer changes. See the
comment at the start of cc-engine.el for more info."
(save-restriction
(widen)
- (let* ((safe-place (c-state-safe-place (point)))
+ (let* ((safe-place (c-state-semi-safe-place (point)))
(lit (c-state-pp-to-literal safe-place (point))))
(or (cadr lit)
(and detect-cpp
@@ -4254,7 +4377,7 @@ comment at the start of cc-engine.el for more info."
(save-excursion
(let* ((pos (point))
- (lim (or lim (c-state-safe-place pos)))
+ (lim (or lim (c-state-semi-safe-place pos)))
(pp-to-lit (save-restriction
(widen)
(c-state-pp-to-literal lim pos)))
@@ -4372,7 +4495,7 @@ comment at the start of cc-engine.el for more info."
;; Get a "safe place" approximately TRY-SIZE characters before START.
;; This doesn't preserve point.
(let* ((pos (max (- start try-size) (point-min)))
- (base (c-state-safe-place pos))
+ (base (c-state-semi-safe-place pos))
(s (parse-partial-sexp base pos)))
(if (or (nth 4 s) (nth 3 s)) ; comment or string
(nth 8 s)