From 56d093a9c8cc3628b028c695c11815f6bb190d3f Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Fri, 2 Mar 2012 22:16:21 +0000 Subject: [PATCH] Depessimize the handling of very large macros. --- lisp/ChangeLog | 22 +++++ lisp/progmodes/cc-engine.el | 233 +++++++++++++++++++++++++++++++++----------- lisp/progmodes/cc-langs.el | 6 +- 3 files changed, 204 insertions(+), 57 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 7e229f8643d..dd4650017d6 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,25 @@ +2012-03-02 Alan Mackenzie + + Depessimize the handling of very large macros. + + * progmodes/cc-engine.el (c-macro-cache, c-macro-cache-start-pos): + (c-macro-cache-syntactic): New variables to implement a one + element macro cache. + (c-invalidate-macro-cache): New function. + (c-beginning-of-macro, c-end-of-macro, c-syntactic-end-of-macro): + Adapt to use the new cache. + (c-state-safe-place): Use better the cache of safe positions. + (c-state-semi-nonlit-pos-cache) + (c-state-semi-nonlit-pos-cache-limit): + New variables for... + (c-state-semi-safe-place): New function. Here, in a macro is "safe". + (c-invalidate-state-cache-1): New stuff for c-state-semi-safe-place. + (c-in-literal, c-literal-limits, c-determine-limit-get-base): Use + c-state-semi-safe-place. + + * progmodes/cc-langs.el (c-get-state-before-change-functions): Add + c-invalidate-macro-cache to the C, C++, Obj entries. + 2012-03-02 Michael Albinus * jka-compr.el (jka-compr-call-process): Apply 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) diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index fafbfb70552..493f3db0961 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -459,8 +459,10 @@ so that all identifiers are recognized as words.") ;; For documentation see the following c-lang-defvar of the same name. ;; The value here may be a list of functions or a single function. t nil - c++ '(c-extend-region-for-CPP c-before-change-check-<>-operators) - (c objc) 'c-extend-region-for-CPP + c++ '(c-extend-region-for-CPP + c-before-change-check-<>-operators + c-invalidate-macro-cache) + (c objc) '(c-extend-region-for-CPP c-invalidate-macro-cache) ;; java 'c-before-change-check-<>-operators awk 'c-awk-record-region-clear-NL) (c-lang-defvar c-get-state-before-change-functions -- 2.11.4.GIT