[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: c mode still slow?
From: |
Alan Mackenzie |
Subject: |
Re: c mode still slow? |
Date: |
Tue, 22 Mar 2011 22:25:20 +0000 |
User-agent: |
Mutt/1.5.9i |
Hi, Dave!
On Mon, Mar 21, 2011 at 07:41:55PM +0000, Alan Mackenzie wrote:
> Hi, Dave!
> On Mon, Mar 21, 2011 at 03:40:51PM +0300, Dave Milter wrote:
> > Hi, Alan and all.
> > Year or two ago I wrote email about emacs usage for C/C++ coding, how
> > it is painfully slow.
> Yes, I remember it well! I think your email was on 1st December 2008,
> and your test file was composed of thousands of #defines without any
> braces. To fix that, I optimised an internal "brace cache" to cope
> with this (reasonable) situation. Unfortunately, there were doubts
> about the absolute correctness of the change (which was large), so it
> sadly didn't get included in Emacs 23.3. It will be in Emacs 24.
> Give me a little time, and I will send you the patch again, in a form
> which can be applied to Emacs 23.3. This will hopefully fix (again) the
> problem with the "#define" file. If the file with the functions is
> still sluggish, I'd like to hear from you again, with the file (if
> possible).
Here is the patch to cc-engine.el that you need. Have fun!
*** cc-engine.el.orig 2011-02-13 20:09:53.000000000 +0000
--- cc-engine.el 2011-03-22 22:09:48.627885672 +0000
***************
*** 1947,1956 ****
;; A system for finding noteworthy parens before the point.
(defvar c-state-cache nil)
(make-variable-buffer-local 'c-state-cache)
;; The state cache used by `c-parse-state' to cut down the amount of
! ;; searching. It's the result from some earlier `c-parse-state' call.
;;
;; The use of the cached info is more effective if the next
;; `c-parse-state' call is on a line close by the one the cached state
--- 1947,1964 ----
;; A system for finding noteworthy parens before the point.
+ (defconst c-state-cache-too-far 5000)
+ ;; A maximum comfortable scanning distance, e.g. between
+ ;; `c-state-cache-good-pos' and "HERE" (where we call c-parse-state). When
+ ;; this distance is exceeded, we take "emergency meausures", e.g. by clearing
+ ;; the cache and starting again from point-min or a beginning of defun. This
+ ;; value can be tuned for efficiency or set to a lower value for testing.
+
(defvar c-state-cache nil)
(make-variable-buffer-local 'c-state-cache)
;; The state cache used by `c-parse-state' to cut down the amount of
! ;; searching. It's the result from some earlier `c-parse-state' call. See
! ;; `c-parse-state''s doc string for details of its structure.
;;
;; The use of the cached info is more effective if the next
;; `c-parse-state' call is on a line close by the one the cached state
***************
*** 1959,1976 ****
;; most effective if `c-parse-state' is used on each line while moving
;; forward.
- (defvar c-state-cache-start 1)
- (make-variable-buffer-local 'c-state-cache-start)
- ;; This is (point-min) when `c-state-cache' was calculated, since a
- ;; change of narrowing is likely to affect the parens that are visible
- ;; before the point.
-
(defvar c-state-cache-good-pos 1)
(make-variable-buffer-local 'c-state-cache-good-pos)
! ;; This is a position where `c-state-cache' is known to be correct.
! ;; It's a position inside one of the recorded unclosed parens or the
! ;; top level, but not further nested inside any literal or subparen
! ;; that is closed before the last recorded position.
;;
;; The exact position is chosen to try to be close to yet earlier than
;; the position where `c-state-cache' will be called next. Right now
--- 1967,1978 ----
;; most effective if `c-parse-state' is used on each line while moving
;; forward.
(defvar c-state-cache-good-pos 1)
(make-variable-buffer-local 'c-state-cache-good-pos)
! ;; This is a position where `c-state-cache' is known to be correct, or
! ;; nil (see below). It's a position inside one of the recorded unclosed
! ;; parens or the top level, but not further nested inside any literal or
! ;; subparen that is closed before the last recorded position.
;;
;; The exact position is chosen to try to be close to yet earlier than
;; the position where `c-state-cache' will be called next. Right now
***************
*** 1978,2290 ****
;; closing paren (of any type) before the line on which
;; `c-parse-state' was called. That is chosen primarily to work well
;; with refontification of the current line.
! (defsubst c-invalidate-state-cache (pos)
! ;; Invalidate all info on `c-state-cache' that applies to the buffer
! ;; at POS or higher. This is much like `c-whack-state-after', but
! ;; it never changes a paren pair element into an open paren element.
! ;; Doing that would mean that the new open paren wouldn't have the
! ;; required preceding paren pair element.
! (while (and (or c-state-cache
! (when (< pos c-state-cache-good-pos)
! (setq c-state-cache-good-pos 1)
! nil))
! (let ((elem (car c-state-cache)))
! (if (consp elem)
! (or (< pos (cdr elem))
! (when (< pos c-state-cache-good-pos)
! (setq c-state-cache-good-pos (cdr elem))
! nil))
! (or (<= pos elem)
! (when (< pos c-state-cache-good-pos)
! (setq c-state-cache-good-pos (1+ elem))
! nil)))))
! (setq c-state-cache (cdr c-state-cache))))
!
! (defun c-get-fallback-start-pos (here)
! ;; Return the start position for building `c-state-cache' from
! ;; scratch.
(save-excursion
;; Go back 2 bods, but ignore any bogus positions returned by
;; beginning-of-defun (i.e. open paren in column zero).
(goto-char here)
(let ((cnt 2))
(while (not (or (bobp) (zerop cnt)))
! (c-beginning-of-defun-1)
(if (eq (char-after) ?\{)
(setq cnt (1- cnt)))))
(point)))
! (defun c-parse-state ()
! ;; Find and record all noteworthy parens between some good point
! ;; earlier in the file and point. That good point is at least the
! ;; beginning of the top-level construct we are in, or the beginning
! ;; of the preceding top-level construct if we aren't in one.
! ;;
! ;; The returned value is a list of the noteworthy parens with the
! ;; last one first. If an element in the list is an integer, it's
! ;; the position of an open paren which has not been closed before
! ;; the point. If an element is a cons, it gives the position of a
! ;; closed brace paren pair; the car is the start paren position and
! ;; the cdr is the position following the closing paren. Only the
! ;; last closed brace paren pair before each open paren and before
! ;; the point is recorded, and thus the state never contains two cons
! ;; elements in succession.
;;
;; Currently no characters which are given paren syntax with the
;; syntax-table property are recorded, i.e. angle bracket arglist
;; parens are never present here. Note that this might change.
;;
;; BUG: This function doesn't cope entirely well with unbalanced
! ;; parens in macros. E.g. in the following case the brace before
! ;; the macro isn't balanced with the one after it:
;;
;; {
;; #define X {
;; }
;;
;; This function might do hidden buffer changes.
! (save-restriction
! (let* ((here (point))
! (here-bol (c-point 'bol))
! (c-macro-start (c-query-macro-start))
! (in-macro-start (or c-macro-start (point)))
! old-state last-pos brace-pair-open brace-pair-close
! pos save-pos)
! (c-invalidate-state-cache here)
!
! ;; If the minimum position has changed due to narrowing then we
! ;; have to fix the tail of `c-state-cache' accordingly.
! (unless (= c-state-cache-start (point-min))
! (if (> (point-min) c-state-cache-start)
! ;; If point-min has moved forward then we just need to cut
! ;; off a bit of the tail.
! (let ((ptr (cons nil c-state-cache)) elem)
! (while (and (setq elem (car-safe (cdr ptr)))
! (>= (if (consp elem) (car elem) elem)
! (point-min)))
! (setq ptr (cdr ptr)))
! (when (consp ptr)
! (if (eq (cdr ptr) c-state-cache)
! (setq c-state-cache nil
! c-state-cache-good-pos 1)
! (setcdr ptr nil))))
! ;; If point-min has moved backward then we drop the state
! ;; completely. It's possible to do a better job here and
! ;; recalculate the top only.
! (setq c-state-cache nil
! c-state-cache-good-pos 1))
! (setq c-state-cache-start (point-min)))
!
! ;; Get the latest position we know are directly inside the
! ;; closest containing paren of the cached state.
! (setq last-pos (and c-state-cache
! (if (consp (car c-state-cache))
! (cdr (car c-state-cache))
! (1+ (car c-state-cache)))))
! (if (or (not last-pos)
! (< last-pos c-state-cache-good-pos))
! (setq last-pos c-state-cache-good-pos)
! ;; Take the opportunity to move the cached good position
! ;; further down.
! (if (< last-pos here-bol)
! (setq c-state-cache-good-pos last-pos)))
!
! ;; Check if `last-pos' is in a macro. If it is, and we're not
! ;; in the same macro, we must discard everything on
! ;; `c-state-cache' that is inside the macro before using it.
! (save-excursion
! (goto-char last-pos)
! (when (and (c-beginning-of-macro)
! (/= (point) in-macro-start))
! (c-invalidate-state-cache (point))
! ;; Set `last-pos' again just like above except that there's
! ;; no use looking at `c-state-cache-good-pos' here.
! (setq last-pos (if c-state-cache
! (if (consp (car c-state-cache))
! (cdr (car c-state-cache))
! (1+ (car c-state-cache)))
! 1))))
!
! ;; If we've moved very far from the last cached position then
! ;; it's probably better to redo it from scratch, otherwise we
! ;; might spend a lot of time searching from `last-pos' down to
! ;; here.
! (when (< last-pos (- here 20000))
! ;; First get the fallback start position. If it turns out
! ;; that it's so far back that the cached state is closer then
! ;; we'll keep it afterall.
! (setq pos (c-get-fallback-start-pos here))
! (if (<= pos last-pos)
! (setq pos nil)
! (setq last-pos nil
! c-state-cache nil
! c-state-cache-good-pos 1)))
!
! ;; Find the start position for the forward search. (Can't
! ;; search in the backward direction since the point might be in
! ;; some kind of literal.)
!
! (unless pos
! (setq old-state c-state-cache)
!
! ;; There's a cached state with a containing paren. Pop off
! ;; the stale containing sexps from it by going forward out of
! ;; parens as far as possible.
(narrow-to-region (point-min) here)
! (let (placeholder pair-beg)
! (while (and c-state-cache
! (setq placeholder
! (c-up-list-forward last-pos)))
! (setq last-pos placeholder)
! (if (consp (car c-state-cache))
! (setq pair-beg (car-safe (cdr c-state-cache))
! c-state-cache (cdr-safe (cdr c-state-cache)))
! (setq pair-beg (car c-state-cache)
! c-state-cache (cdr c-state-cache))))
!
! (when (and pair-beg (eq (char-after pair-beg) ?{))
! ;; The last paren pair we moved out from was a brace
! ;; pair. Modify the state to record this as a closed
! ;; pair now.
! (if (consp (car-safe c-state-cache))
! (setq c-state-cache (cdr c-state-cache)))
! (setq c-state-cache (cons (cons pair-beg last-pos)
! c-state-cache))))
!
! ;; Check if the preceding balanced paren is within a
! ;; macro; it should be ignored if we're outside the
! ;; macro. There's no need to check any further upwards;
! ;; if the macro contains an unbalanced opening paren then
! ;; we're smoked anyway.
! (when (and (<= (point) in-macro-start)
! (consp (car c-state-cache)))
! (save-excursion
! (goto-char (car (car c-state-cache)))
! (when (c-beginning-of-macro)
! (setq here (point)
! c-state-cache (cdr c-state-cache)))))
!
! (unless (eq c-state-cache old-state)
! ;; Have to adjust the cached good position if state has been
! ;; popped off.
! (setq c-state-cache-good-pos
! (if c-state-cache
! (if (consp (car c-state-cache))
! (cdr (car c-state-cache))
! (1+ (car c-state-cache)))
! 1)
! old-state c-state-cache))
!
! (when c-state-cache
! (setq pos last-pos)))
!
! ;; Get the fallback start position.
! (unless pos
! (setq pos (c-get-fallback-start-pos here)
! c-state-cache nil
! c-state-cache-good-pos 1))
!
! (narrow-to-region (point-min) here)
!
! (while pos
! (setq save-pos pos
! brace-pair-open nil)
! ;; Find the balanced brace pairs. This loop is hot, so it
! ;; does ugly tricks to go faster.
! (c-safe
! (let (set-good-pos set-brace-pair)
! (while t
! (setq last-pos nil
! last-pos (scan-lists pos 1 -1)) ; Might signal.
! (setq pos (scan-lists last-pos 1 1) ; Might signal.
! set-good-pos (< pos here-bol)
! set-brace-pair (eq (char-before last-pos) ?{))
!
! ;; Update the cached good position and record the brace
! ;; pair, whichever is applicable for the paren we've
! ;; just jumped over. But first check that it isn't
! ;; inside a macro and the point isn't inside the same
! ;; one.
! (when (and (or set-good-pos set-brace-pair)
! (or (>= pos in-macro-start)
! (save-excursion
! (goto-char pos)
! (not (c-beginning-of-macro)))))
! (if set-good-pos
! (setq c-state-cache-good-pos pos))
! (if set-brace-pair
! (setq brace-pair-open last-pos
! brace-pair-close pos))))))
!
! ;; Record the last brace pair.
! (when brace-pair-open
! (let ((head (car-safe c-state-cache)))
! (if (consp head)
! (progn
! (setcar head (1- brace-pair-open))
! (setcdr head brace-pair-close))
! (setq c-state-cache (cons (cons (1- brace-pair-open)
! brace-pair-close)
! c-state-cache)))))
!
! (if last-pos
! ;; Prepare to loop, but record the open paren only if it's
! ;; outside a macro or within the same macro as point, and
! ;; if it is a legitimate open paren and not some character
! ;; that got an open paren syntax-table property.
! (progn
! (setq pos last-pos)
! (when (and (or (>= last-pos in-macro-start)
! (save-excursion
! (goto-char last-pos)
! (not (c-beginning-of-macro))))
! ;; Check for known types of parens that we
! ;; want to record. The syntax table is not to
! ;; be trusted here since the caller might be
! ;; using e.g. `c++-template-syntax-table'.
! (memq (char-before last-pos) '(?{ ?\( ?\[)))
! (if (< last-pos here-bol)
! (setq c-state-cache-good-pos last-pos))
! (setq c-state-cache (cons (1- last-pos) c-state-cache))))
!
! (if (setq last-pos (c-up-list-forward pos))
! ;; Found a close paren without a corresponding opening
! ;; one. Maybe we didn't go back far enough, so try to
! ;; scan backward for the start paren and then start over.
! (progn
! (setq pos (c-up-list-backward pos)
! c-state-cache nil
! c-state-cache-good-pos c-state-cache-start)
! (when (or (not pos)
! ;; Emacs (up to at least 21.2) can get confused by
! ;; open parens in column zero inside comments: The
! ;; sexp functions can then misbehave and bring us
! ;; back to the same point again. Check this so that
! ;; we don't get an infinite loop.
! (>= pos save-pos))
! (setq pos last-pos
! c-parsing-error
! (format "Unbalanced close paren at line %d"
! (1+ (count-lines (point-min)
! (c-point 'bol last-pos)))))))
! (setq pos nil))))
!
! ;;(message "c-parse-state: %S end: %S" c-state-cache
c-state-cache-good-pos)
! c-state-cache)))
! ;; Debug tool to catch cache inconsistencies.
(defvar c-debug-parse-state nil)
(unless (fboundp 'c-real-parse-state)
(fset 'c-real-parse-state (symbol-function 'c-parse-state)))
(cc-bytecomp-defun c-real-parse-state)
(defun c-debug-parse-state ()
! (let ((res1 (c-real-parse-state)) res2)
(let ((c-state-cache nil)
! (c-state-cache-start 1)
! (c-state-cache-good-pos 1))
(setq res2 (c-real-parse-state)))
(unless (equal res1 res2)
;; The cache can actually go further back due to the ad-hoc way
--- 1980,3087 ----
;; closing paren (of any type) before the line on which
;; `c-parse-state' was called. That is chosen primarily to work well
;; with refontification of the current line.
+ ;;
+ ;; 2009-07-28: When `c-state-point-min' and the last position where
+ ;; `c-parse-state' or for which `c-invalidate-state-cache' was called, are
+ ;; both in the same literal, there is no such "good position", and
+ ;; c-state-cache-good-pos is then nil. This is the ONLY circumstance in which
+ ;; it can be nil. In this case, `c-state-point-min-literal' will be non-nil.
+ ;;
+ ;; 2009-06-12: In a brace desert, c-state-cache-good-pos may also be in
+ ;; the middle of the desert, as long as it is not within a brace pair
+ ;; recorded in `c-state-cache' or a paren/bracket pair.
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; We maintain a simple cache of positions which aren't in a literal, so as to
+ ;; speed up testing for non-literality.
+ (defconst c-state-nonlit-pos-interval 10000)
+ ;; The approximate interval between entries in `c-state-nonlit-pos-cache'.
+
+ (defvar c-state-nonlit-pos-cache nil)
+ (make-variable-buffer-local 'c-state-nonlit-pos-cache)
+ ;; A list of buffer positions which are known not to be in a literal or a cpp
+ ;; construct. This is ordered with higher positions at the front of the list.
+ ;; Only those which are less than `c-state-nonlit-pos-cache-limit' are valid.
+
+ (defvar c-state-nonlit-pos-cache-limit 1)
+ (make-variable-buffer-local 'c-state-nonlit-pos-cache-limit)
+ ;; An upper limit on valid entries in `c-state-nonlit-pos-cache'. This is
+ ;; reduced by buffer changes, and increased by invocations of
+ ;; `c-state-literal-at'.
+
+ (defsubst c-state-pp-to-literal (from to)
+ ;; Do a parse-partial-sexp from FROM to TO, returning the bounds of any
+ ;; literal at TO as a cons, otherwise NIL.
+ ;; FROM must not be in a literal, and the buffer should already be wide
+ ;; enough.
+ (save-excursion
+ (let ((s (parse-partial-sexp from to)))
+ (when (or (nth 3 s) (nth 4 s)) ; in a string or comment
+ (parse-partial-sexp (point) (point-max)
+ nil ; TARGETDEPTH
+ nil ; STOPBEFORE
+ s ; OLDSTATE
+ 'syntax-table) ; stop at end of literal
+ (cons (nth 8 s) (point))))))
+
+ (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
+ ;; buffer). Otherwise, return nil.
+ ;;
+ ;; This function is almost the same as `c-literal-limits'. It differs in
+ ;; that it is a lower level function, and that it rigourously follows the
+ ;; syntax from BOB, whereas `c-literal-limits' uses a "local" safe position.
+ ;;
+ ;; NOTE: This function manipulates `c-state-nonlit-pos-cache'. This cache
+ ;; MAY NOT contain any positions within macros, since macros are frequently
+ ;; turned into comments by use of the `c-cpp-delimiter' category properties.
+ ;; We cannot rely on this mechanism whilst determining a cache pos since
+ ;; this function is also called from outwith `c-parse-state'.
+ (save-restriction
+ (widen)
+ (save-excursion
+ (let ((c c-state-nonlit-pos-cache)
+ pos npos lit)
+ ;; 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 c (cdr c)))
+ (setq pos (or (car c) (point-min)))
+
+ (while (<= (setq npos (+ pos c-state-nonlit-pos-interval))
+ here)
+ (setq lit (c-state-pp-to-literal pos npos))
+ (setq pos (or (cdr lit) npos)) ; end of literal containing npos.
+ (goto-char pos)
+ (when (and (c-beginning-of-macro) (/= (point) pos))
+ (c-syntactic-end-of-macro)
+ (or (eobp) (forward-char))
+ (setq pos (point)))
+ (setq c-state-nonlit-pos-cache (cons pos c-state-nonlit-pos-cache)))
+
+ (if (> pos c-state-nonlit-pos-cache-limit)
+ (setq c-state-nonlit-pos-cache-limit pos))
+ (if (< pos here)
+ (setq lit (c-state-pp-to-literal pos here)))
+ lit))))
+
+ (defsubst c-state-lit-beg (pos)
+ ;; Return the start of the literal containing POS, or POS itself.
+ (or (car (c-state-literal-at pos))
+ pos))
+
+ (defsubst c-state-cache-non-literal-place (pos state)
+ ;; Return a position outside of a string/comment at or before POS.
+ ;; STATE is the parse-partial-sexp state at POS.
+ (if (or (nth 3 state) ; in a string?
+ (nth 4 state)) ; in a comment?
+ (nth 8 state)
+ pos))
!
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; Stuff to do with point-min, and coping with any literal there.
! (defvar c-state-point-min 1)
! (make-variable-buffer-local 'c-state-point-min)
! ;; This is (point-min) when `c-state-cache' was last calculated. A change of
! ;; narrowing is likely to affect the parens that are visible before the point.
!
! (defvar c-state-point-min-lit-type nil)
! (make-variable-buffer-local 'c-state-point-min-lit-type)
! (defvar c-state-point-min-lit-start nil)
! (make-variable-buffer-local 'c-state-point-min-lit-start)
! ;; These two variables define the literal, if any, containing point-min.
! ;; Their values are, respectively, 'string, c, or c++, and the start of the
! ;; literal. If there's no literal there, they're both nil.
!
! (defvar c-state-min-scan-pos 1)
! (make-variable-buffer-local 'c-state-min-scan-pos)
! ;; This is the earliest buffer-pos from which scanning can be done. It is
! ;; either the end of the literal containing point-min, or point-min itself.
! ;; It becomes nil if the buffer is changed earlier than this point.
! (defun c-state-get-min-scan-pos ()
! ;; Return the lowest valid scanning pos. This will be the end of the
! ;; literal enclosing point-min, or point-min itself.
! (or c-state-min-scan-pos
! (save-restriction
! (save-excursion
! (widen)
! (goto-char c-state-point-min-lit-start)
! (if (eq c-state-point-min-lit-type 'string)
! (forward-sexp)
! (forward-comment 1))
! (setq c-state-min-scan-pos (point))))))
!
! (defun c-state-mark-point-min-literal ()
! ;; Determine the properties of any literal containing POINT-MIN, setting the
! ;; variables `c-state-point-min-lit-type', `c-state-point-min-lit-start',
! ;; and `c-state-min-scan-pos' accordingly. The return value is meaningless.
! (let ((p-min (point-min))
! lit)
! (save-restriction
! (widen)
! (setq lit (c-state-literal-at p-min))
! (if lit
! (setq c-state-point-min-lit-type
! (save-excursion
! (goto-char (car lit))
! (cond
! ((looking-at c-block-comment-start-regexp) 'c)
! ((looking-at c-line-comment-starter) 'c++)
! (t 'string)))
! c-state-point-min-lit-start (car lit)
! c-state-min-scan-pos (cdr lit))
! (setq c-state-point-min-lit-type nil
! c-state-point-min-lit-start nil
! c-state-min-scan-pos p-min)))))
!
!
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; A variable which signals a brace dessert - helpful for reducing the number
! ;; of fruitless backward scans.
! (defvar c-state-brace-pair-desert nil)
! (make-variable-buffer-local 'c-state-brace-pair-desert)
! ;; Used only in `c-append-lower-brace-pair-to-state-cache'. It is set when
! ;; that defun has searched backwards for a brace pair and not found one. Its
! ;; value is either nil or a cons (PA . FROM), where PA is the position of the
! ;; enclosing opening paren/brace/bracket which bounds the backwards search (or
! ;; nil when at top level) and FROM is where the backward search started. It
! ;; is reset to nil in `c-invalidate-state-cache'.
!
!
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; Lowish level functions/macros which work directly on `c-state-cache', or a
! ;; list of like structure.
! (defmacro c-state-cache-top-lparen (&optional cache)
! ;; Return the address of the top left brace/bracket/paren recorded in CACHE
! ;; (default `c-state-cache') (or nil).
! (let ((cash (or cache 'c-state-cache)))
! `(if (consp (car ,cash))
! (caar ,cash)
! (car ,cash))))
!
! (defmacro c-state-cache-top-paren (&optional cache)
! ;; Return the address of the latest brace/bracket/paren (whether left or
! ;; right) recorded in CACHE (default `c-state-cache') or nil.
! (let ((cash (or cache 'c-state-cache)))
! `(if (consp (car ,cash))
! (cdar ,cash)
! (car ,cash))))
!
! (defmacro c-state-cache-after-top-paren (&optional cache)
! ;; Return the position just after the latest brace/bracket/paren (whether
! ;; left or right) recorded in CACHE (default `c-state-cache') or nil.
! (let ((cash (or cache 'c-state-cache)))
! `(if (consp (car ,cash))
! (cdar ,cash)
! (and (car ,cash)
! (1+ (car ,cash))))))
!
! (defun c-get-cache-scan-pos (here)
! ;; From the state-cache, determine the buffer position from which we might
! ;; scan forward to HERE to update this cache. This position will be just
! ;; after a paren/brace/bracket recorded in the cache, if possible, otherwise
! ;; return the earliest position in the accessible region which isn't within
! ;; a literal. If the visible portion of the buffer is entirely within a
! ;; literal, return NIL.
! (let ((c c-state-cache) elt)
! ;(while (>= (or (c-state-cache-top-lparen c) 1) here)
! (while (and c
! (>= (c-state-cache-top-lparen c) here))
! (setq c (cdr c)))
!
! (setq elt (car c))
! (cond
! ((consp elt)
! (if (> (cdr elt) here)
! (1+ (car elt))
! (cdr elt)))
! (elt (1+ elt))
! ((<= (c-state-get-min-scan-pos) here)
! (c-state-get-min-scan-pos))
! (t nil))))
!
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; Variables which keep track of preprocessor constructs.
! (defvar c-state-old-cpp-beg nil)
! (make-variable-buffer-local 'c-state-old-cpp-beg)
! (defvar c-state-old-cpp-end nil)
! (make-variable-buffer-local 'c-state-old-cpp-end)
! ;; These are the limits of the macro containing point at the previous call of
! ;; `c-parse-state', or nil.
!
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; Defuns which analyse the buffer, yet don't change `c-state-cache'.
! (defun c-get-fallback-scan-pos (here)
! ;; Return a start position for building `c-state-cache' from
! ;; scratch. This will be at the top level, 2 defuns back.
(save-excursion
;; Go back 2 bods, but ignore any bogus positions returned by
;; beginning-of-defun (i.e. open paren in column zero).
(goto-char here)
(let ((cnt 2))
(while (not (or (bobp) (zerop cnt)))
! (c-beginning-of-defun-1) ; Pure elisp BOD.
(if (eq (char-after) ?\{)
(setq cnt (1- cnt)))))
(point)))
! (defun c-state-balance-parens-backwards (here- here+ top)
! ;; Return the position of the opening paren/brace/bracket before HERE- which
! ;; matches the outermost close p/b/b between HERE+ and TOP. Except when
! ;; there's a macro, HERE- and HERE+ are the same. Like this:
! ;;
! ;; ............................................
! ;; | |
! ;; ( [ ( .........#macro.. ) ( ) ] )
! ;; ^ ^ ^ ^
! ;; | | | |
! ;; return HERE- HERE+ TOP
! ;;
! ;; If there aren't enough opening paren/brace/brackets, return the position
! ;; of the outermost one found, or HERE- if there are none. If there are no
! ;; closeing p/b/bs between HERE+ and TOP, return HERE-. HERE-/+ and TOP
! ;; must not be inside literals. Only the accessible portion of the buffer
! ;; will be scanned.
!
! ;; PART 1: scan from `here+' up to `top', accumulating ")"s which enclose
! ;; `here'. Go round the next loop each time we pass over such a ")".
These
! ;; probably match "("s before `here-'.
! (let (pos pa ren+1 lonely-rens)
! (save-excursion
! (save-restriction
! (narrow-to-region (point-min) top) ; This can move point, sometimes.
! (setq pos here+)
! (c-safe
! (while
! (setq ren+1 (scan-lists pos 1 1)) ; might signal
! (setq lonely-rens (cons ren+1 lonely-rens)
! pos ren+1)))))
!
! ;; PART 2: Scan back before `here-' searching for the "("s
! ;; matching/mismatching the ")"s found above. We only need to direct the
! ;; caller to scan when we've encountered unmatched right parens.
! (setq pos here-)
! (when lonely-rens
! (c-safe
! (while
! (and lonely-rens ; actual values aren't used.
! (setq pa (scan-lists pos -1 1)))
! (setq pos pa)
! (setq lonely-rens (cdr lonely-rens)))))
! pos))
!
! (defun c-parse-state-get-strategy (here good-pos)
! ;; Determine the scanning strategy for adjusting `c-parse-state', attempting
! ;; to minimise the amount of scanning. HERE is the pertinent position in
! ;; the buffer, GOOD-POS is a position where `c-state-cache' (possibly with
! ;; its head trimmed) is known to be good, or nil if there is no such
! ;; position.
! ;;
! ;; The return value is a list, one of the following:
! ;;
! ;; o - ('forward CACHE-POS START-POINT) - scan forward from START-POINT,
! ;; which is not less than CACHE-POS.
! ;; o - ('backward CACHE-POS nil) - scan backwards (from HERE).
! ;; o - ('BOD nil START-POINT) - scan forwards from START-POINT, which is at
the
! ;; top level.
! ;; o - ('IN-LIT nil nil) - point is inside the literal containing point-min.
! ;; , where CACHE-POS is the highest position recorded in `c-state-cache' at
! ;; or below HERE.
! (let ((cache-pos (c-get-cache-scan-pos here)) ; highest position
below HERE in cache (or 1)
! BOD-pos ; position of 2nd BOD before HERE.
! strategy ; 'forward, 'backward, 'BOD, or 'IN-LIT.
! start-point
! how-far) ; putative scanning distance.
! (setq good-pos (or good-pos (c-state-get-min-scan-pos)))
! (cond
! ((< here (c-state-get-min-scan-pos))
! (setq strategy 'IN-LIT
! start-point nil
! cache-pos nil
! how-far 0))
! ((<= good-pos here)
! (setq strategy 'forward
! start-point (max good-pos cache-pos)
! how-far (- here start-point)))
! ((< (- good-pos here) (- here cache-pos)) ; FIXME!!! ; apply some sort
of weighting.
! (setq strategy 'backward
! how-far (- good-pos here)))
! (t
! (setq strategy 'forward
! how-far (- here cache-pos)
! start-point cache-pos)))
!
! ;; Might we be better off starting from the top level, two defuns back,
! ;; instead?
! (when (> how-far c-state-cache-too-far)
! (setq BOD-pos (c-get-fallback-scan-pos here)) ; somewhat EXPENSIVE!!!
! (if (< (- here BOD-pos) how-far)
! (setq strategy 'BOD
! start-point BOD-pos)))
!
! (list
! strategy
! (and (memq strategy '(forward backward)) cache-pos)
! (and (memq strategy '(forward BOD)) start-point))))
!
!
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; Routines which change `c-state-cache' and associated values.
! (defun c-renarrow-state-cache ()
! ;; The region (more precisely, point-min) has changed since we
! ;; calculated `c-state-cache'. Amend `c-state-cache' accordingly.
! (if (< (point-min) c-state-point-min)
! ;; If point-min has MOVED BACKWARDS then we drop the state completely.
! ;; It would be possible to do a better job here and recalculate the top
! ;; only.
! (progn
! (c-state-mark-point-min-literal)
! (setq c-state-cache nil
! c-state-cache-good-pos c-state-min-scan-pos
! c-state-brace-pair-desert nil))
!
! ;; point-min has MOVED FORWARD.
!
! ;; Is the new point-min inside a (different) literal?
! (unless (and c-state-point-min-lit-start ; at prev. point-min
! (< (point-min) (c-state-get-min-scan-pos)))
! (c-state-mark-point-min-literal))
!
! ;; Cut off a bit of the tail from `c-state-cache'.
! (let ((ptr (cons nil c-state-cache))
! pa)
! (while (and (setq pa (c-state-cache-top-lparen (cdr ptr)))
! (>= pa (point-min)))
! (setq ptr (cdr ptr)))
!
! (when (consp ptr)
! (if (eq (cdr ptr) c-state-cache)
! (setq c-state-cache nil
! c-state-cache-good-pos c-state-min-scan-pos)
! (setcdr ptr nil)
! (setq c-state-cache-good-pos (1+ (c-state-cache-top-lparen))))
! )))
!
! (setq c-state-point-min (point-min)))
!
! (defun c-append-lower-brace-pair-to-state-cache (from &optional upper-lim)
! ;; If there is a brace pair preceding FROM in the buffer (not necessarily
! ;; immediately preceding), push a cons onto `c-state-cache' to represent it.
! ;; FROM must not be inside a literal. If UPPER-LIM is non-nil, we append
! ;; the highest brace pair whose "}" is below UPPER-LIM.
! ;;
! ;; Return non-nil when this has been done.
! ;;
! ;; This routine should be fast. Since it can get called a LOT, we maintain
! ;; `c-state-brace-pair-desert', a small cache of "failures", such that we
! ;; reduce the time wasted in repeated fruitless searches in brace deserts.
! (save-excursion
! (save-restriction
! (let ((bra from) ce ; Positions of "{" and "}".
! new-cons
! (cache-pos (c-state-cache-top-lparen)) ; might be nil.
! (macro-start-or-from
! (progn (goto-char from)
! (c-beginning-of-macro)
! (point))))
! (or upper-lim (setq upper-lim from))
!
! ;; If we're essentially repeating a fruitless search, just give up.
! (unless (and c-state-brace-pair-desert
! (eq cache-pos (car c-state-brace-pair-desert))
! (<= from (cdr c-state-brace-pair-desert)))
! ;; Only search what we absolutely need to:
! (if (and c-state-brace-pair-desert
! (> from (cdr c-state-brace-pair-desert)))
! (narrow-to-region (cdr c-state-brace-pair-desert) (point-max)))
!
! ;; In the next pair of nested loops, the inner one moves back past a
! ;; pair of (mis-)matching parens or brackets; the outer one moves
! ;; back over a sequence of unmatched close brace/paren/bracket each
! ;; time round.
! (while
! (progn
! (c-safe
! (while
! (and (setq ce (scan-lists bra -1 -1)) ; back past )/]/};
might signal
! (setq bra (scan-lists ce -1 1)) ; back past (/[/{;
might signal
! (or (> ce upper-lim)
! (not (eq (char-after bra) ?\{))
! (and (goto-char bra)
! (c-beginning-of-macro)
! (< (point) macro-start-or-from))))))
! (and ce (< ce bra)))
! (setq bra ce)) ; If we just backed over an unbalanced closing
! ; brace, ignore it.
!
! (if (and ce (< bra ce) (eq (char-after bra) ?\{))
! ;; We've found the desired brace-pair.
! (progn
! (setq new-cons (cons bra (1+ ce)))
! (cond
! ((consp (car c-state-cache))
! (setcar c-state-cache new-cons))
! ((and (numberp (car c-state-cache)) ; probably never happens
! (< ce (car c-state-cache)))
! (setcdr c-state-cache
! (cons new-cons (cdr c-state-cache))))
! (t (setq c-state-cache (cons new-cons c-state-cache)))))
!
! ;; We haven't found a brace pair. Record this.
! (setq c-state-brace-pair-desert (cons cache-pos from))))))))
!
! (defsubst c-state-push-any-brace-pair (bra+1 macro-start-or-here)
! ;; If BRA+1 is nil, do nothing. Otherwise, BRA+1 is the buffer position
! ;; following a {, and that brace has a (mis-)matching } (or ]), and we
! ;; "push" "a" brace pair onto `c-state-cache'.
! ;;
! ;; Here "push" means overwrite the top element if it's itself a brace-pair,
! ;; otherwise push it normally.
! ;;
! ;; The brace pair we push is normally the one surrounding BRA+1, but if the
! ;; latter is inside a macro, not being a macro containing
! ;; MACRO-START-OR-HERE, we scan backwards through the buffer for a non-macro
! ;; base pair. This latter case is assumed to be rare.
! ;;
! ;; Note: POINT is not preserved in this routine.
! (if bra+1
! (if (or (> bra+1 macro-start-or-here)
! (progn (goto-char bra+1)
! (not (c-beginning-of-macro))))
! (setq c-state-cache
! (cons (cons (1- bra+1)
! (scan-lists bra+1 1 1))
! (if (consp (car c-state-cache))
! (cdr c-state-cache)
! c-state-cache)))
! ;; N.B. This defsubst codes one method for the simple, normal case,
! ;; and a more sophisticated, slower way for the general case. Don't
! ;; eliminate this defsubst - it's a speed optimisation.
! (c-append-lower-brace-pair-to-state-cache (1- bra+1)))))
!
! (defun c-append-to-state-cache (from)
! ;; Scan the buffer from FROM to (point-max), adding elements into
! ;; `c-state-cache' for braces etc. Return a candidate for
! ;; `c-state-cache-good-pos'.
! ;;
! ;; FROM must be after the latest brace/paren/bracket in `c-state-cache', if
! ;; any. Typically, it is immediately after it. It must not be inside a
! ;; literal.
! (let ((here-bol (c-point 'bol (point-max)))
! (macro-start-or-here
! (save-excursion (goto-char (point-max))
! (if (c-beginning-of-macro)
! (point)
! (point-max))))
! pa+1 ; pos just after an opening PAren (or brace).
! (ren+1 from) ; usually a pos just after an closing paREN etc.
! ; Is actually the pos. to scan for a (/{/[ from,
! ; which sometimes is after a silly )/}/].
! paren+1 ; Pos after some opening or closing paren.
! paren+1s ; A list of `paren+1's; used to determine a
! ; good-pos.
! bra+1 ce+1 ; just after L/R bra-ces.
! bra+1s ; list of OLD values of bra+1.
! mstart) ; start of a macro.
!
! (save-excursion
! ;; Each time round the following loop, we enter a succesively deeper
! ;; level of brace/paren nesting. (Except sometimes we "continue at
! ;; the existing level".) `pa+1' is a pos inside an opening
! ;; brace/paren/bracket, usually just after it.
! (while
! (progn
! ;; Each time round the next loop moves forward over an opening then
! ;; a closing brace/bracket/paren. This loop is white hot, so it
! ;; plays ugly tricks to go fast. DON'T PUT ANYTHING INTO THIS
! ;; LOOP WHICH ISN'T ABSOLUTELY NECESSARY!!! It terminates when a
! ;; call of `scan-lists' signals an error, which happens when there
! ;; are no more b/b/p's to scan.
! (c-safe
! (while t
! (setq pa+1 (scan-lists ren+1 1 -1) ; Into (/{/[; might signal
! paren+1s (cons pa+1 paren+1s))
! (setq ren+1 (scan-lists pa+1 1 1)) ; Out of )/}/]; might signal
! (if (and (eq (char-before pa+1) ?{)) ; Check for a macro later.
! (setq bra+1 pa+1))
! (setcar paren+1s ren+1)))
!
! (if (and pa+1 (> pa+1 ren+1))
! ;; We've just entered a deeper nesting level.
! (progn
! ;; Insert the brace pair (if present) and the single open
! ;; paren/brace/bracket into `c-state-cache' It cannot be
! ;; inside a macro, except one around point, because of what
! ;; `c-neutralize-syntax-in-CPP' has done.
! (c-state-push-any-brace-pair bra+1 macro-start-or-here)
! ;; Insert the opening brace/bracket/paren position.
! (setq c-state-cache (cons (1- pa+1) c-state-cache))
! ;; Clear admin stuff for the next more nested part of the
scan.
! (setq ren+1 pa+1 pa+1 nil bra+1 nil bra+1s nil)
! t) ; Carry on the loop
!
! ;; All open p/b/b's at this nesting level, if any, have probably
! ;; been closed by matching/mismatching ones. We're probably
! ;; finished - we just need to check for having found an
! ;; unmatched )/}/], which we ignore. Such a )/}/] can't be in a
! ;; macro, due the action of `c-neutralize-syntax-in-CPP'.
! (c-safe (setq ren+1 (scan-lists ren+1 1 1)))))) ; acts as loop
control.
!
! ;; Record the final, innermost, brace-pair if there is one.
! (c-state-push-any-brace-pair bra+1 macro-start-or-here)
!
! ;; Determine a good pos
! (while (and (setq paren+1 (car paren+1s))
! (> (if (> paren+1 macro-start-or-here)
! paren+1
! (goto-char paren+1)
! (setq mstart (and (c-beginning-of-macro)
! (point)))
! (or mstart paren+1))
! here-bol))
! (setq paren+1s (cdr paren+1s)))
! (cond
! ((and paren+1 mstart)
! (min paren+1 mstart))
! (paren+1)
! (t from)))))
!
! (defun c-remove-stale-state-cache (good-pos pps-point)
! ;; Remove stale entries from the `c-cache-state', i.e. those which will
! ;; not be in it when it is amended for position (point-max).
! ;; Additionally, the "outermost" open-brace entry before (point-max)
! ;; will be converted to a cons if the matching close-brace is scanned.
! ;;
! ;; GOOD-POS is a "maximal" "safe position" - there must be no open
! ;; parens/braces/brackets between GOOD-POS and (point-max).
! ;;
! ;; As a second thing, calculate the result of parse-partial-sexp at
! ;; PPS-POINT, w.r.t. GOOD-POS. The motivation here is that
! ;; `c-state-cache-good-pos' may become PPS-POINT, but the caller may need to
! ;; adjust it to get outside a string/comment. (Sorry about this! The code
! ;; needs to be FAST).
! ;;
! ;; Return a list (GOOD-POS SCAN-BACK-POS PPS-STATE), where
! ;; o - GOOD-POS is a position where the new value `c-state-cache' is known
! ;; to be good (we aim for this to be as high as possible);
! ;; o - SCAN-BACK-POS, if not nil, indicates there may be a brace pair
! ;; preceding POS which needs to be recorded in `c-state-cache'. It is a
! ;; position to scan backwards from.
! ;; o - PPS-STATE is the parse-partial-sexp state at PPS-POINT.
! (save-restriction
! (narrow-to-region 1 (point-max))
! (save-excursion
! (let* ((in-macro-start ; start of macro containing (point-max) or nil.
! (save-excursion
! (goto-char (point-max))
! (and (c-beginning-of-macro)
! (point))))
! (good-pos-actual-macro-start ; Start of macro containing good-pos
! ; or nil
! (and (< good-pos (point-max))
! (save-excursion
! (goto-char good-pos)
! (and (c-beginning-of-macro)
! (point)))))
! (good-pos-actual-macro-end ; End of this macro, (maybe
! ; (point-max)), or nil.
! (and good-pos-actual-macro-start
! (save-excursion
! (goto-char good-pos-actual-macro-start)
! (c-end-of-macro)
! (point))))
! pps-state ; Will be 9 or 10 elements long.
! pos
! upper-lim ; ,beyond which `c-state-cache' entries are removed
! scan-back-pos
! pair-beg pps-point-state target-depth)
!
! ;; Remove entries beyond (point-max). Also remove any entries inside
! ;; a macro, unless (point-max) is in the same macro.
! (setq upper-lim
! (if (or (null c-state-old-cpp-beg)
! (and (> (point-max) c-state-old-cpp-beg)
! (< (point-max) c-state-old-cpp-end)))
! (point-max)
! (min (point-max) c-state-old-cpp-beg)))
! (while (and c-state-cache (>= (c-state-cache-top-lparen) upper-lim))
! (setq c-state-cache (cdr c-state-cache)))
! ;; If `upper-lim' is inside the last recorded brace pair, remove its
! ;; RBrace and indicate we'll need to search backwards for a previous
! ;; brace pair.
! (when (and c-state-cache
! (consp (car c-state-cache))
! (> (cdar c-state-cache) upper-lim))
! (setcar c-state-cache (caar c-state-cache))
! (setq scan-back-pos (car c-state-cache)))
!
! ;; The next loop jumps forward out of a nested level of parens each
! ;; time round; the corresponding elements in `c-state-cache' are
! ;; removed. `pos' is just after the brace-pair or the open paren at
! ;; (car c-state-cache). There can be no open parens/braces/brackets
! ;; between `good-pos'/`good-pos-actual-macro-start' and (point-max),
! ;; due to the interface spec to this function.
! (setq pos (if (and good-pos-actual-macro-end
! (not (eq good-pos-actual-macro-start
! in-macro-start)))
! (1+ good-pos-actual-macro-end) ; get outside the macro as
! ; marked by a `category' text property.
! good-pos))
! (goto-char pos)
! (while (and c-state-cache
! (< (point) (point-max)))
! (cond
! ((null pps-state) ; first time through
! (setq target-depth -1))
! ((eq (car pps-state) target-depth) ; found closing ),},]
! (setq target-depth (1- (car pps-state))))
! ;; Do nothing when we've merely reached pps-point.
! )
!
! ;; Scan!
! (setq pps-state
! (parse-partial-sexp
! (point) (if (< (point) pps-point) pps-point (point-max))
! target-depth
! nil pps-state))
!
! (if (= (point) pps-point)
! (setq pps-point-state pps-state))
!
! (when (eq (car pps-state) target-depth)
! (setq pos (point)) ; POS is now just after an R-paren/brace.
! (cond
! ((and (consp (car c-state-cache))
! (eq (point) (cdar c-state-cache)))
! ;; We've just moved out of the paren pair containing the
brace-pair
! ;; at (car c-state-cache). `pair-beg' is where the open paren
is,
! ;; and is potentially where the open brace of a cons in
! ;; c-state-cache will be.
! (setq pair-beg (car-safe (cdr c-state-cache))
! c-state-cache (cdr-safe (cdr c-state-cache)))) ; remove
{}pair + containing Lparen.
! ((numberp (car c-state-cache))
! (setq pair-beg (car c-state-cache)
! c-state-cache (cdr c-state-cache))) ; remove this
! ; containing Lparen
! ((numberp (cadr c-state-cache))
! (setq pair-beg (cadr c-state-cache)
! c-state-cache (cddr c-state-cache))) ; Remove a paren pair
! ; together with enclosed brace pair.
! ;; (t nil) ; Ignore an unmated Rparen.
! )))
!
! (if (< (point) pps-point)
! (setq pps-state (parse-partial-sexp (point) pps-point
! nil nil ; TARGETDEPTH,
STOPBEFORE
! pps-state)))
!
! ;; If the last paren pair we moved out of was actually a brace pair,
! ;; insert it into `c-state-cache'.
! (when (and pair-beg (eq (char-after pair-beg) ?{))
! (if (consp (car-safe c-state-cache))
! (setq c-state-cache (cdr c-state-cache)))
! (setq c-state-cache (cons (cons pair-beg pos)
! c-state-cache)))
!
! (list pos scan-back-pos pps-state)))))
!
! (defun c-remove-stale-state-cache-backwards (here cache-pos)
! ;; Strip stale elements of `c-state-cache' by moving backwards through the
! ;; buffer, and inform the caller of the scenario detected.
! ;;
! ;; HERE is the position we're setting `c-state-cache' for.
! ;; CACHE-POS is just after the latest recorded position in `c-state-cache'
! ;; before HERE, or a position at or near point-min which isn't in a
! ;; literal.
! ;;
! ;; This function must only be called only when (> `c-state-cache-good-pos'
! ;; HERE). Usually the gap between CACHE-POS and HERE is large. It is thus
! ;; optimised to eliminate (or minimise) scanning between these two
! ;; positions.
! ;;
! ;; Return a three element list (GOOD-POS SCAN-BACK-POS FWD-FLAG), where:
! ;; o - GOOD-POS is a "good position", where `c-state-cache' is valid, or
! ;; could become so after missing elements are inserted into
! ;; `c-state-cache'. This is JUST AFTER an opening or closing
! ;; brace/paren/bracket which is already in `c-state-cache' or just before
! ;; one otherwise. exceptionally (when there's no such b/p/b handy) the
BOL
! ;; before `here''s line, or the start of the literal containing it.
! ;; o - SCAN-BACK-POS, if non-nil, indicates there may be a brace pair
! ;; preceding POS which isn't recorded in `c-state-cache'. It is a
position
! ;; to scan backwards from.
! ;; o - FWD-FLAG, if non-nil, indicates there may be parens/braces between
! ;; POS and HERE which aren't recorded in `c-state-cache'.
! ;;
! ;; The comments in this defun use "paren" to mean parenthesis or square
! ;; bracket (as contrasted with a brace), and "(" and ")" likewise.
! ;;
! ;; . {..} (..) (..) ( .. { } ) (...) ( .... . ..)
! ;; | | | | | |
! ;; CP E here D C good
! (let ((pos c-state-cache-good-pos)
! pa ren ; positions of "(" and ")"
! dropped-cons ; whether the last element dropped from `c-state-cache'
! ; was a cons (representing a brace-pair)
! good-pos ; see above.
! lit ; (START . END) of a literal containing some point.
! here-lit-start here-lit-end ; bounds of literal containing `here'
! ; or `here' itself.
! here- here+ ; start/end of macro around HERE, or HERE
! (here-bol (c-point 'bol here))
! (too-far-back (max (- here c-state-cache-too-far) 1)))
!
! ;; Remove completely irrelevant entries from `c-state-cache'.
! (while (and c-state-cache
! (>= (setq pa (c-state-cache-top-lparen)) here))
! (setq dropped-cons (consp (car c-state-cache)))
! (setq c-state-cache (cdr c-state-cache))
! (setq pos pa))
! ;; At this stage, (> pos here);
! ;; (< (c-state-cache-top-lparen) here) (or is nil).
!
! (cond
! ((and (consp (car c-state-cache))
! (> (cdar c-state-cache) here))
! ;; CASE 1: The top of the cache is a brace pair which now encloses
! ;; `here'. As good-pos, return the address. of the "{". Since we've no
! ;; knowledge of what's inside these braces, we have no alternative but
! ;; to direct the caller to scan the buffer from the opening brace.
! (setq pos (caar c-state-cache))
! (setcar c-state-cache pos)
! (list (1+ pos) pos t)) ; return value. We've just converted a brace
pair
! ; entry into a { entry, so the caller needs to
! ; search for a brace pair before the {.
!
! ;; `here' might be inside a literal. Check for this.
! ((progn
! (setq lit (c-state-literal-at here)
! here-lit-start (or (car lit) here)
! here-lit-end (or (cdr lit) here))
! ;; Has `here' just "newly entered" a macro?
! (save-excursion
! (goto-char here-lit-start)
! (if (and (c-beginning-of-macro)
! (or (null c-state-old-cpp-beg)
! (not (= (point) c-state-old-cpp-beg))))
! (progn
! (setq here- (point))
! (c-end-of-macro)
! (setq here+ (point)))
! (setq here- here-lit-start
! here+ here-lit-end)))
!
! ;; `here' might be nested inside any depth of parens (or brackets but
! ;; not braces). Scan backwards to find the outermost such opening
! ;; paren, if there is one. This will be the scan position to return.
! (save-restriction
! (narrow-to-region cache-pos (point-max))
! (setq pos (c-state-balance-parens-backwards here- here+ pos)))
! nil)) ; for the cond
!
! ((< pos here-lit-start)
! ;; CASE 2: Address of outermost ( or [ which now encloses `here', but
! ;; didn't enclose the (previous) `c-state-cache-good-pos'. If there is
! ;; a brace pair preceding this, it will already be in `c-state-cache',
! ;; unless there was a brace pair after it, i.e. there'll only be one to
! ;; scan for if we've just deleted one.
! (list pos (and dropped-cons pos) t)) ; Return value.
!
! ;; `here' isn't enclosed in a (previously unrecorded) bracket/paren.
! ;; Further forward scanning isn't needed, but we still need to find a
! ;; GOOD-POS. Step out of all enclosing "("s on HERE's line.
! ((progn
! (save-restriction
! (narrow-to-region here-bol (point-max))
! (setq pos here-lit-start)
! (c-safe (while (setq pa (scan-lists pos -1 1))
! (setq pos pa)))) ; might signal
! nil)) ; for the cond
!
! ((setq ren (c-safe-scan-lists pos -1 -1 too-far-back))
! ;; CASE 3: After a }/)/] before `here''s BOL.
! (list (1+ ren) (and dropped-cons pos) nil)) ; Return value
!
! (t
! ;; CASE 4; Best of a bad job: BOL before `here-bol', or beginning of
! ;; literal containing it.
! (setq good-pos (c-state-lit-beg (c-point 'bopl here-bol)))
! (list good-pos (and dropped-cons good-pos) nil)))))
!
!
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; Externally visible routines.
!
! (defun c-state-cache-init ()
! (setq c-state-cache nil
! c-state-cache-good-pos 1
! c-state-nonlit-pos-cache nil
! c-state-nonlit-pos-cache-limit 1
! c-state-brace-pair-desert nil
! c-state-point-min 1
! c-state-point-min-lit-type nil
! c-state-point-min-lit-start nil
! c-state-min-scan-pos 1
! c-state-old-cpp-beg nil
! c-state-old-cpp-end nil)
! (c-state-mark-point-min-literal))
!
!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! ;; Debugging routines to dump `c-state-cache' in a "replayable" form.
! ;; (defmacro c-sc-de (elt) ; "c-state-cache-dump-element"
! ;; `(format ,(concat "(setq " (symbol-name elt) " %s) ") ,elt))
! ;; (defmacro c-sc-qde (elt) ; "c-state-cache-quote-dump-element"
! ;; `(format ,(concat "(setq " (symbol-name elt) " '%s) ") ,elt))
! ;; (defun c-state-dump ()
! ;; ;; For debugging.
! ;; ;(message
! ;; (concat
! ;; (c-sc-qde c-state-cache)
! ;; (c-sc-de c-state-cache-good-pos)
! ;; (c-sc-qde c-state-nonlit-pos-cache)
! ;; (c-sc-de c-state-nonlit-pos-cache-limit)
! ;; (c-sc-qde c-state-brace-pair-desert)
! ;; (c-sc-de c-state-point-min)
! ;; (c-sc-de c-state-point-min-lit-type)
! ;; (c-sc-de c-state-point-min-lit-start)
! ;; (c-sc-de c-state-min-scan-pos)
! ;; (c-sc-de c-state-old-cpp-beg)
! ;; (c-sc-de c-state-old-cpp-end)))
! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
!
! (defun c-invalidate-state-cache-1 (here)
! ;; Invalidate all info on `c-state-cache' that applies to the buffer at HERE
! ;; or higher and set `c-state-cache-good-pos' accordingly. The cache is
! ;; left in a consistent state.
! ;;
! ;; This is much like `c-whack-state-after', but it never changes a paren
! ;; pair element into an open paren element. Doing that would mean that the
! ;; new open paren wouldn't have the required preceding paren pair element.
! ;;
! ;; This function is called from c-after-change.
!
! ;; The cache of non-literals:
! (if (< here c-state-nonlit-pos-cache-limit)
! (setq c-state-nonlit-pos-cache-limit here))
!
! ;; `c-state-cache':
! ;; Case 1: if `here' is in a literal containing point-min, everything
! ;; becomes (or is already) nil.
! (if (or (null c-state-cache-good-pos)
! (< here (c-state-get-min-scan-pos)))
! (setq c-state-cache nil
! c-state-cache-good-pos nil
! c-state-min-scan-pos nil)
!
! ;;; Truncate `c-state-cache' and set `c-state-cache-good-pos' to a value below
! ;;; `here'. To maintain its consistency, we may need to insert a new brace
! ;;; pair.
! (let ((here-bol (c-point 'bol here))
! too-high-pa ; recorded {/(/[ next above here, or nil.
! dropped-cons ; was the last removed element a brace pair?
! pa)
! ;; The easy bit - knock over-the-top bits off `c-state-cache'.
! (while (and c-state-cache
! (>= (setq pa (c-state-cache-top-paren)) here))
! (setq dropped-cons (consp (car c-state-cache))
! too-high-pa (c-state-cache-top-lparen)
! c-state-cache (cdr c-state-cache)))
!
! ;; Do we need to add in an earlier brace pair, having lopped one off?
! (if (and dropped-cons
! (< too-high-pa (+ here c-state-cache-too-far)))
! (c-append-lower-brace-pair-to-state-cache too-high-pa here-bol))
! (setq c-state-cache-good-pos (or (c-state-cache-after-top-paren)
! (c-state-get-min-scan-pos)))))
!
! ;; The brace-pair desert marker:
! (when (car c-state-brace-pair-desert)
! (if (< here (car c-state-brace-pair-desert))
! (setq c-state-brace-pair-desert nil)
! (if (< here (cdr c-state-brace-pair-desert))
! (setcdr c-state-brace-pair-desert here)))))
!
! (defun c-parse-state-1 ()
! ;; Find and record all noteworthy parens between some good point earlier in
! ;; the file and point. That good point is at least the beginning of the
! ;; top-level construct we are in, or the beginning of the preceding
! ;; top-level construct if we aren't in one.
! ;;
! ;; The returned value is a list of the noteworthy parens with the last one
! ;; first. If an element in the list is an integer, it's the position of an
! ;; open paren (of any type) which has not been closed before the point. If
! ;; an element is a cons, it gives the position of a closed BRACE paren
! ;; pair[*]; the car is the start brace position and the cdr is the position
! ;; following the closing brace. Only the last closed brace paren pair
! ;; before each open paren and before the point is recorded, and thus the
! ;; state never contains two cons elements in succession. When a close brace
! ;; has no matching open brace (e.g., the matching brace is outside the
! ;; visible region), it is not represented in the returned value.
! ;;
! ;; [*] N.B. The close "brace" might be a mismatching close bracket or paren.
! ;; This defun explicitly treats mismatching parens/braces/brackets as
! ;; matching. It is the open brace which makes it a "brace" pair.
! ;;
! ;; If POINT is within a macro, open parens and brace pairs within
! ;; THIS macro MIGHT be recorded. This depends on whether their
! ;; syntactic properties have been suppressed by
! ;; `c-neutralize-syntax-in-CPP'. This might need fixing (2008-12-11).
;;
;; Currently no characters which are given paren syntax with the
;; syntax-table property are recorded, i.e. angle bracket arglist
;; parens are never present here. Note that this might change.
;;
;; BUG: This function doesn't cope entirely well with unbalanced
! ;; parens in macros. (2008-12-11: this has probably been resolved
! ;; by the function `c-neutralize-syntax-in-CPP'.) E.g. in the
! ;; following case the brace before the macro isn't balanced with the
! ;; one after it:
;;
;; {
;; #define X {
;; }
;;
+ ;; Note to maintainers: this function DOES get called with point
+ ;; within comments and strings, so don't assume it doesn't!
+ ;;
;; This function might do hidden buffer changes.
+ (let* ((here (point))
+ (here-bopl (c-point 'bopl))
+ strategy ; 'forward, 'backward etc..
+ ;; Candidate positions to start scanning from:
+ cache-pos ; highest position below HERE already existing in
+ ; cache (or 1).
+ good-pos
+ start-point
+ bopl-state
+ res
+ scan-backward-pos scan-forward-p) ; used for 'backward.
+ ;; If POINT-MIN has changed, adjust the cache
+ (unless (= (point-min) c-state-point-min)
+ (c-renarrow-state-cache))
+
+ ;; Strategy?
+ (setq res (c-parse-state-get-strategy here c-state-cache-good-pos)
+ strategy (car res)
+ cache-pos (cadr res)
+ start-point (nth 2 res))
+
+ (when (eq strategy 'BOD)
+ (setq c-state-cache nil
+ c-state-cache-good-pos start-point))
! ;; SCAN!
! (save-restriction
! (cond
! ((memq strategy '(forward BOD))
(narrow-to-region (point-min) here)
! (setq res (c-remove-stale-state-cache start-point here-bopl))
! (setq cache-pos (car res)
! scan-backward-pos (cadr res)
! bopl-state (car (cddr res))) ; will be nil if (< here-bopl
! ; start-point)
! (if scan-backward-pos
! (c-append-lower-brace-pair-to-state-cache scan-backward-pos))
! (setq good-pos
! (c-append-to-state-cache cache-pos))
! (setq c-state-cache-good-pos
! (if (and bopl-state
! (< good-pos (- here c-state-cache-too-far)))
! (c-state-cache-non-literal-place here-bopl bopl-state)
! good-pos)))
!
! ((eq strategy 'backward)
! (setq res (c-remove-stale-state-cache-backwards here cache-pos)
! good-pos (car res)
! scan-backward-pos (cadr res)
! scan-forward-p (car (cddr res)))
! (if scan-backward-pos
! (c-append-lower-brace-pair-to-state-cache
! scan-backward-pos))
! (setq c-state-cache-good-pos
! (if scan-forward-p
! (progn (narrow-to-region (point-min) here)
! (c-append-to-state-cache good-pos))
!
! (c-get-cache-scan-pos good-pos))))
!
! (t ; (eq strategy 'IN-LIT)
! (setq c-state-cache nil
! c-state-cache-good-pos nil)))))
!
! c-state-cache)
!
! (defun c-invalidate-state-cache (here)
! ;; This is a wrapper over `c-invalidate-state-cache-1'.
! ;;
! ;; It suppresses the syntactic effect of the < and > (template) brackets and
! ;; of all parens in preprocessor constructs, except for any such construct
! ;; containing point. We can then call `c-invalidate-state-cache-1' without
! ;; worrying further about macros and template delimiters.
! (c-with-<->-as-parens-suppressed
! (if (and c-state-old-cpp-beg
! (< c-state-old-cpp-beg here))
! (c-with-all-but-one-cpps-commented-out
! c-state-old-cpp-beg
! (min c-state-old-cpp-end here)
! (c-invalidate-state-cache-1 here))
! (c-with-cpps-commented-out
! (c-invalidate-state-cache-1 here)))))
! (defun c-parse-state ()
! ;; This is a wrapper over `c-parse-state-1'. See that function for a
! ;; description of the functionality and return value.
! ;;
! ;; It suppresses the syntactic effect of the < and > (template) brackets and
! ;; of all parens in preprocessor constructs, except for any such construct
! ;; containing point. We can then call `c-parse-state-1' without worrying
! ;; further about macros and template delimiters.
! (let (here-cpp-beg here-cpp-end)
! (save-excursion
! (when (c-beginning-of-macro)
! (setq here-cpp-beg (point))
! (unless
! (> (setq here-cpp-end (c-syntactic-end-of-macro))
! here-cpp-beg)
! (setq here-cpp-beg nil here-cpp-end nil))))
! ;; FIXME!!! Put in a `condition-case' here to protect the integrity of the
! ;; subsystem.
! (prog1
! (c-with-<->-as-parens-suppressed
! (if (and here-cpp-beg (> here-cpp-end here-cpp-beg))
! (c-with-all-but-one-cpps-commented-out
! here-cpp-beg here-cpp-end
! (c-parse-state-1))
! (c-with-cpps-commented-out
! (c-parse-state-1))))
! (setq c-state-old-cpp-beg (and here-cpp-beg (copy-marker here-cpp-beg
t))
! c-state-old-cpp-end (and here-cpp-end (copy-marker here-cpp-end t)))
! )))
! ;; Debug tool to catch cache inconsistencies. This is called from
! ;; 000tests.el.
(defvar c-debug-parse-state nil)
(unless (fboundp 'c-real-parse-state)
(fset 'c-real-parse-state (symbol-function 'c-parse-state)))
(cc-bytecomp-defun c-real-parse-state)
(defun c-debug-parse-state ()
! (let ((here (point)) (res1 (c-real-parse-state)) res2)
(let ((c-state-cache nil)
! (c-state-cache-good-pos 1)
! (c-state-nonlit-pos-cache nil)
! (c-state-nonlit-pos-cache-limit 1)
! (c-state-brace-pair-desert nil)
! (c-state-point-min 1)
! (c-state-point-min-lit-type nil)
! (c-state-point-min-lit-start nil)
! (c-state-min-scan-pos 1)
! (c-state-old-cpp-beg nil)
! (c-state-old-cpp-end nil))
(setq res2 (c-real-parse-state)))
(unless (equal res1 res2)
;; The cache can actually go further back due to the ad-hoc way
***************
*** 2296,2305 ****
(while (not (or (bobp) (eq (char-after) ?{)))
(c-beginning-of-defun-1))
(unless (equal (c-whack-state-before (point) res1) res2)
! (message (concat "c-parse-state inconsistency: "
"using cache: %s, from scratch: %s")
! res1 res2))))
res1))
(defun c-toggle-parse-state-debug (&optional arg)
(interactive "P")
(setq c-debug-parse-state (c-calculate-state arg c-debug-parse-state))
--- 3093,3103 ----
(while (not (or (bobp) (eq (char-after) ?{)))
(c-beginning-of-defun-1))
(unless (equal (c-whack-state-before (point) res1) res2)
! (message (concat "c-parse-state inconsistency at %s: "
"using cache: %s, from scratch: %s")
! here res1 res2))))
res1))
+
(defun c-toggle-parse-state-debug (&optional arg)
(interactive "P")
(setq c-debug-parse-state (c-calculate-state arg c-debug-parse-state))
***************
*** 2310,2315 ****
--- 3108,3114 ----
(when c-debug-parse-state
(c-toggle-parse-state-debug 1))
+
(defun c-whack-state-before (bufpos paren-state)
;; Whack off any state information from PAREN-STATE which lies
;; before BUFPOS. Not destructive on PAREN-STATE.
> --
> Alan Mackenzie (Nuremberg, Germany).