Fix a cacheing bug, which led to inordinately slow c-beginning-of-defun.

* lisp/progmodes/cc-defs.el (c-self-bind-state-cache): New macro.

* lisp/progmodes/cc-engine.el (c-ssb-lit-begin): Always call c-parse-state
rather than just using the cache variable c-state-cache.
(c-syntactic-skip-backward): Invoke c-self-bind-state-cache to isolate calls
to c-parse-state from other uses of the parse state cache.

* lisp/progmodes/cc-cmds.el (c-beginning-of-defun, c-end-of-defun): Invoke
c-self-bind-state-cache around the processing, replacing flawed bindings of
c-state-cache.
This commit is contained in:
Alan Mackenzie 2016-03-14 21:44:11 +00:00
parent 0ce37eac45
commit 5cc6919308
3 changed files with 225 additions and 201 deletions

View file

@ -1594,69 +1594,70 @@ defun."
(c-region-is-active-p) (c-region-is-active-p)
(push-mark)) (push-mark))
(c-save-buffer-state (c-self-bind-state-cache ; We must not share with other users of c-state-cache.
(beginning-of-defun-function end-of-defun-function (c-save-buffer-state
(start (point)) (beginning-of-defun-function
(paren-state (copy-tree (c-parse-state))) ; This must not share list end-of-defun-function
; structure with other users of c-state-cache. (start (point))
(orig-point-min (point-min)) (orig-point-max (point-max)) (paren-state (c-parse-state))
lim ; Position of { which has been widened to. (orig-point-min (point-min)) (orig-point-max (point-max))
where pos case-fold-search) lim ; Position of { which has been widened to.
where pos case-fold-search)
(save-restriction (save-restriction
(if (eq c-defun-tactic 'go-outward) (if (eq c-defun-tactic 'go-outward)
(setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace. (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace.
paren-state orig-point-min orig-point-max))) paren-state orig-point-min orig-point-max)))
;; Move back out of any macro/comment/string we happen to be in. ;; Move back out of any macro/comment/string we happen to be in.
(c-beginning-of-macro) (c-beginning-of-macro)
(setq pos (c-literal-limits)) (setq pos (c-literal-limits))
(if pos (goto-char (car pos))) (if pos (goto-char (car pos)))
(setq where (c-where-wrt-brace-construct)) (setq where (c-where-wrt-brace-construct))
(if (< arg 0) (if (< arg 0)
;; Move forward to the closing brace of a function. ;; Move forward to the closing brace of a function.
(progn (progn
(if (memq where '(at-function-end outwith-function)) (if (memq where '(at-function-end outwith-function))
(setq arg (1+ arg))) (setq arg (1+ arg)))
(if (< arg 0) (if (< arg 0)
(c-while-widening-to-decl-block (c-while-widening-to-decl-block
(< (setq arg (- (c-forward-to-nth-EOF-} (- arg) where))) 0))) (< (setq arg (- (c-forward-to-nth-EOF-} (- arg) where))) 0)))
;; Move forward to the next opening brace.... ;; Move forward to the next opening brace....
(when (and (= arg 0) (when (and (= arg 0)
(progn (progn
(c-while-widening-to-decl-block (c-while-widening-to-decl-block
(not (c-syntactic-re-search-forward "{" nil 'eob))) (not (c-syntactic-re-search-forward "{" nil 'eob)))
(eq (char-before) ?{))) (eq (char-before) ?{)))
(backward-char) (backward-char)
;; ... and backward to the function header. ;; ... and backward to the function header.
(c-beginning-of-decl-1) (c-beginning-of-decl-1)
t)) t))
;; Move backward to the opening brace of a function, making successively ;; Move backward to the opening brace of a function, making successively
;; larger portions of the buffer visible as necessary. ;; larger portions of the buffer visible as necessary.
(when (> arg 0) (when (> arg 0)
(c-while-widening-to-decl-block (c-while-widening-to-decl-block
(> (setq arg (c-backward-to-nth-BOF-{ arg where)) 0))) (> (setq arg (c-backward-to-nth-BOF-{ arg where)) 0)))
(when (eq arg 0) (when (eq arg 0)
;; Go backward to this function's header. ;; Go backward to this function's header.
(c-beginning-of-decl-1) (c-beginning-of-decl-1)
(setq pos (point)) (setq pos (point))
;; We're now there, modulo comments and whitespace. ;; We're now there, modulo comments and whitespace.
;; Try to be line oriented; position point at the closest ;; Try to be line oriented; position point at the closest
;; preceding boi that isn't inside a comment, but if we hit ;; preceding boi that isn't inside a comment, but if we hit
;; the previous declaration then we use the current point ;; the previous declaration then we use the current point
;; instead. ;; instead.
(while (and (/= (point) (c-point 'boi)) (while (and (/= (point) (c-point 'boi))
(c-backward-single-comment))) (c-backward-single-comment)))
(if (/= (point) (c-point 'boi)) (if (/= (point) (c-point 'boi))
(goto-char pos))) (goto-char pos)))
(c-keep-region-active) (c-keep-region-active)
(= arg 0))))) (= arg 0))))))
(defun c-forward-to-nth-EOF-} (n where) (defun c-forward-to-nth-EOF-} (n where)
;; Skip to the closing brace of the Nth function after point. If ;; Skip to the closing brace of the Nth function after point. If
@ -1718,66 +1719,68 @@ the open-parenthesis that starts a defun; see `beginning-of-defun'."
(c-region-is-active-p) (c-region-is-active-p)
(push-mark)) (push-mark))
(c-save-buffer-state (c-self-bind-state-cache ; c-state-cache's list structure must not be shared
(beginning-of-defun-function end-of-defun-function ; with other users.
(start (point)) (c-save-buffer-state
(paren-state (copy-tree (c-parse-state))) ; This must not share list (beginning-of-defun-function
; structure with other users of c-state-cache. end-of-defun-function
(orig-point-min (point-min)) (orig-point-max (point-max)) (start (point))
lim (paren-state (c-parse-state))
where pos case-fold-search) (orig-point-min (point-min)) (orig-point-max (point-max))
lim
where pos case-fold-search)
(save-restriction (save-restriction
(if (eq c-defun-tactic 'go-outward) (if (eq c-defun-tactic 'go-outward)
(setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace (setq lim (c-widen-to-enclosing-decl-scope ; e.g. class, namespace
paren-state orig-point-min orig-point-max))) paren-state orig-point-min orig-point-max)))
;; Move back out of any macro/comment/string we happen to be in. ;; Move back out of any macro/comment/string we happen to be in.
(c-beginning-of-macro) (c-beginning-of-macro)
(setq pos (c-literal-limits)) (setq pos (c-literal-limits))
(if pos (goto-char (car pos))) (if pos (goto-char (car pos)))
(setq where (c-where-wrt-brace-construct)) (setq where (c-where-wrt-brace-construct))
(if (< arg 0) (if (< arg 0)
;; Move backwards to the } of a function ;; Move backwards to the } of a function
(progn (progn
(if (memq where '(at-header outwith-function)) (if (memq where '(at-header outwith-function))
(setq arg (1+ arg))) (setq arg (1+ arg)))
(if (< arg 0) (if (< arg 0)
(c-while-widening-to-decl-block (c-while-widening-to-decl-block
(< (setq arg (- (c-backward-to-nth-BOF-{ (- arg) where))) 0))) (< (setq arg (- (c-backward-to-nth-BOF-{ (- arg) where))) 0)))
(if (= arg 0) (if (= arg 0)
(c-while-widening-to-decl-block (c-while-widening-to-decl-block
(progn (c-syntactic-skip-backward "^}") (progn (c-syntactic-skip-backward "^}")
(not (eq (char-before) ?})))))) (not (eq (char-before) ?}))))))
;; Move forward to the } of a function ;; Move forward to the } of a function
(if (> arg 0) (if (> arg 0)
(c-while-widening-to-decl-block (c-while-widening-to-decl-block
(> (setq arg (c-forward-to-nth-EOF-} arg where)) 0)))) (> (setq arg (c-forward-to-nth-EOF-} arg where)) 0))))
;; Do we need to move forward from the brace to the semicolon? ;; Do we need to move forward from the brace to the semicolon?
(when (eq arg 0) (when (eq arg 0)
(if (c-in-function-trailer-p) ; after "}" of struct/enum, etc. (if (c-in-function-trailer-p) ; after "}" of struct/enum, etc.
(c-syntactic-re-search-forward ";")) (c-syntactic-re-search-forward ";"))
(setq pos (point)) (setq pos (point))
;; We're there now, modulo comments and whitespace. ;; We're there now, modulo comments and whitespace.
;; Try to be line oriented; position point after the next ;; Try to be line oriented; position point after the next
;; newline that isn't inside a comment, but if we hit the ;; newline that isn't inside a comment, but if we hit the
;; next declaration then we use the current point instead. ;; next declaration then we use the current point instead.
(while (and (not (bolp)) (while (and (not (bolp))
(not (looking-at "\\s *$")) (not (looking-at "\\s *$"))
(c-forward-single-comment))) (c-forward-single-comment)))
(cond ((bolp)) (cond ((bolp))
((looking-at "\\s *$") ((looking-at "\\s *$")
(forward-line 1)) (forward-line 1))
(t (t
(goto-char pos)))) (goto-char pos))))
(c-keep-region-active) (c-keep-region-active)
(= arg 0)))) (= arg 0)))))
(defun c-defun-name () (defun c-defun-name ()
"Return the name of the current defun, or NIL if there isn't one. "Return the name of the current defun, or NIL if there isn't one.

View file

@ -1258,7 +1258,8 @@ been put there by c-put-char-property. POINT remains unchanged."
(def-edebug-spec c-clear-char-property t) (def-edebug-spec c-clear-char-property t)
(def-edebug-spec c-clear-char-properties t) (def-edebug-spec c-clear-char-properties t)
(def-edebug-spec c-put-overlay t) (def-edebug-spec c-put-overlay t)
(def-edebug-spec c-delete-overlay t) ;)) (def-edebug-spec c-delete-overlay t)
(def-edebug-spec c-self-bind-state-cache t);))
;;; Functions. ;;; Functions.
@ -1397,6 +1398,26 @@ been put there by c-put-char-property. POINT remains unchanged."
(save-restriction (save-restriction
(widen) (widen)
(c-set-cpp-delimiters ,beg ,end))))) (c-set-cpp-delimiters ,beg ,end)))))
(defmacro c-self-bind-state-cache (&rest forms)
;; Bind the state cache to itself and execute the FORMS. It is assumed that no
;; buffer changes will happen in FORMS, and no hidden buffer changes which could
;; affect the parsing will be made by FORMS.
`(let ((c-state-cache (copy-tree c-state-cache))
(c-state-cache-good-pos c-state-cache-good-pos)
;(c-state-nonlit-pos-cache (copy-tree c-state-nonlit-pos-cache))
;(c-state-nonlit-pos-cache-limit c-state-nonlit-pos-cache-limit)
;(c-state-semi-nonlit-pos-cache (copy-treec c-state-semi-nonlit-pos-cache))
;(c-state-semi-nonlit-pos-cache-limit c-state-semi-nonlit-pos-cache)
(c-state-brace-pair-desert (copy-tree c-state-brace-pair-desert))
(c-state-point-min c-state-point-min)
(c-state-point-min-lit-type c-state-point-min-lit-type)
(c-state-point-min-lit-start c-state-point-min-lit-start)
(c-state-min-scan-pos c-state-min-scan-pos)
(c-state-old-cpp-beg c-state-old-cpp-beg)
(c-state-old-cpp-end c-state-old-cpp-end))
,@forms))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The following macros are to be used only in `c-parse-state' and its ;; The following macros are to be used only in `c-parse-state' and its

View file

@ -4259,8 +4259,7 @@ comment at the start of cc-engine.el for more info."
(setq safe-pos-list (cdr safe-pos-list))) (setq safe-pos-list (cdr safe-pos-list)))
(unless (setq safe-pos (car-safe safe-pos-list)) (unless (setq safe-pos (car-safe safe-pos-list))
(setq safe-pos (max (or (c-safe-position (setq safe-pos (max (or (c-safe-position
(point) (or c-state-cache (point) (c-parse-state))
(c-parse-state)))
0) 0)
(point-min)) (point-min))
safe-pos-list (list safe-pos))) safe-pos-list (list safe-pos)))
@ -4308,107 +4307,108 @@ Non-nil is returned if the point moved, nil otherwise.
Note that this function might do hidden buffer changes. See the Note that this function might do hidden buffer changes. See the
comment at the start of cc-engine.el for more info." comment at the start of cc-engine.el for more info."
(let ((start (point)) (c-self-bind-state-cache
state-2 (let ((start (point))
;; A list of syntactically relevant positions in descending state-2
;; order. It's used to avoid scanning repeatedly over ;; A list of syntactically relevant positions in descending
;; potentially large regions with `parse-partial-sexp' to verify ;; order. It's used to avoid scanning repeatedly over
;; each position. Used in `c-ssb-lit-begin' ;; potentially large regions with `parse-partial-sexp' to verify
safe-pos-list ;; each position. Used in `c-ssb-lit-begin'
;; The result from `c-beginning-of-macro' at the start position or the safe-pos-list
;; start position itself if it isn't within a macro. Evaluated on ;; The result from `c-beginning-of-macro' at the start position or the
;; demand. ;; start position itself if it isn't within a macro. Evaluated on
start-macro-beg ;; demand.
;; The earliest position after the current one with the same paren start-macro-beg
;; level. Used only when `paren-level' is set. ;; The earliest position after the current one with the same paren
lit-beg ;; level. Used only when `paren-level' is set.
(paren-level-pos (point))) lit-beg
(paren-level-pos (point)))
(while (while
(progn (progn
;; The next loop "tries" to find the end point each time round, ;; The next loop "tries" to find the end point each time round,
;; loops when it hasn't succeeded. ;; loops when it hasn't succeeded.
(while (while
(and (and
(let ((pos (point))) (let ((pos (point)))
(while (and (while (and
(< (skip-chars-backward skip-chars limit) 0) (< (skip-chars-backward skip-chars limit) 0)
;; Don't stop inside a literal. ;; Don't stop inside a literal.
(when (setq lit-beg (c-ssb-lit-begin)) (when (setq lit-beg (c-ssb-lit-begin))
(goto-char lit-beg) (goto-char lit-beg)
t))) t)))
(< (point) pos)) (< (point) pos))
(let ((pos (point)) state-2 pps-end-pos) (let ((pos (point)) state-2 pps-end-pos)
(cond (cond
((and paren-level ((and paren-level
(save-excursion (save-excursion
(setq state-2 (parse-partial-sexp (setq state-2 (parse-partial-sexp
pos paren-level-pos -1) pos paren-level-pos -1)
pps-end-pos (point)) pps-end-pos (point))
(/= (car state-2) 0))) (/= (car state-2) 0)))
;; Not at the right level. ;; Not at the right level.
(if (and (< (car state-2) 0) (if (and (< (car state-2) 0)
;; We stop above if we go out of a paren. ;; We stop above if we go out of a paren.
;; Now check whether it precedes or is ;; Now check whether it precedes or is
;; nested in the starting sexp. ;; nested in the starting sexp.
(save-excursion (save-excursion
(setq state-2 (setq state-2
(parse-partial-sexp (parse-partial-sexp
pps-end-pos paren-level-pos pps-end-pos paren-level-pos
nil nil state-2)) nil nil state-2))
(< (car state-2) 0))) (< (car state-2) 0)))
;; We've stopped short of the starting position ;; We've stopped short of the starting position
;; so the hit was inside a nested list. Go up ;; so the hit was inside a nested list. Go up
;; until we are at the right level. ;; until we are at the right level.
(condition-case nil (condition-case nil
(progn (progn
(goto-char (scan-lists pos -1 (goto-char (scan-lists pos -1
(- (car state-2)))) (- (car state-2))))
(setq paren-level-pos (point)) (setq paren-level-pos (point))
(if (and limit (>= limit paren-level-pos)) (if (and limit (>= limit paren-level-pos))
(progn (progn
(goto-char limit) (goto-char limit)
nil) nil)
t)) t))
(error (error
(goto-char (or limit (point-min))) (goto-char (or limit (point-min)))
nil)) nil))
;; The hit was outside the list at the start ;; The hit was outside the list at the start
;; position. Go to the start of the list and exit. ;; position. Go to the start of the list and exit.
(goto-char (1+ (elt state-2 1))) (goto-char (1+ (elt state-2 1)))
nil)) nil))
((c-beginning-of-macro limit) ((c-beginning-of-macro limit)
;; Inside a macro. ;; Inside a macro.
(if (< (point) (if (< (point)
(or start-macro-beg (or start-macro-beg
(setq start-macro-beg (setq start-macro-beg
(save-excursion (save-excursion
(goto-char start) (goto-char start)
(c-beginning-of-macro limit) (c-beginning-of-macro limit)
(point))))) (point)))))
t t
;; It's inside the same macro we started in so it's ;; It's inside the same macro we started in so it's
;; a relevant match. ;; a relevant match.
(goto-char pos) (goto-char pos)
nil)))))) nil))))))
(> (point) (> (point)
(progn (progn
;; Skip syntactic ws afterwards so that we don't stop at the ;; Skip syntactic ws afterwards so that we don't stop at the
;; end of a comment if `skip-chars' is something like "^/". ;; end of a comment if `skip-chars' is something like "^/".
(c-backward-syntactic-ws) (c-backward-syntactic-ws)
(point))))) (point)))))
;; We might want to extend this with more useful return values in ;; We might want to extend this with more useful return values in
;; the future. ;; the future.
(/= (point) start))) (/= (point) start))))
;; The following is an alternative implementation of ;; The following is an alternative implementation of
;; `c-syntactic-skip-backward' that uses backward movement to keep ;; `c-syntactic-skip-backward' that uses backward movement to keep