;;; cc-engine.el --- core syntax guessing engine for CC mode
-;; Copyright (C) 1985, 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-;; 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1987, 1992-2011 Free Software Foundation, Inc.
;; Authors: 2001- Alan Mackenzie
;; 1998- Martin Stjernholm
;; 1985 Richard M. Stallman
;; Maintainer: bug-cc-mode@gnu.org
;; Created: 22-Apr-1997 (split from cc-mode.el)
-;; Version: See cc-mode.el
-;; Keywords: c languages oop
+;; Keywords: c languages
+;; Package: cc-mode
;; This file is part of GNU Emacs.
;; Note: This doc is for internal use only. Other packages should not
;; assume that these text properties are used as described here.
;;
+;; 'category
+;; Used for "indirection". With its help, some other property can
+;; be cheaply and easily switched on or off everywhere it occurs.
+;;
;; 'syntax-table
;; Used to modify the syntax of some characters. It is used to
;; mark the "<" and ">" of angle bracket parens with paren syntax, and
(forward-char)
t))))
+(defun c-syntactic-end-of-macro ()
+ ;; Go to the end of a CPP directive, or a "safe" pos just before.
+ ;;
+ ;; This is normally the end of the next non-escaped line. A "safe"
+ ;; position is one not within a string or comment. (The EOL on a line
+ ;; comment is NOT "safe").
+ ;;
+ ;; This function must only be called from the beginning of a CPP construct.
+ ;;
+ ;; Note that this function might do hidden buffer changes. See the comment
+ ;; 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)))
+ (point)))
+
(defun c-forward-over-cpp-define-id ()
;; Assuming point is at the "#" that introduces a preprocessor
;; directive, it's moved forward to the end of the identifier which is
\f
;; 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.
+;; 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
;; 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.
+;; 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
;; 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. 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.
+ (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.
+ (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))
-(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.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 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 an
+;; 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)
+ (c-beginning-of-defun-1) ; Pure elisp BOD.
(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.
+(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))
+
+(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. E.g. in the following case the brace before
- ;; the macro isn't balanced with the one after it:
+ ;; 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)))))
- (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)))
+(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)))
+ )))
- ;; 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.
+;; 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 ((res1 (c-real-parse-state)) res2)
+ (let ((here (point)) (res1 (c-real-parse-state)) res2)
(let ((c-state-cache nil)
- (c-state-cache-start 1)
- (c-state-cache-good-pos 1))
+ (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
(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: "
+ (message (concat "c-parse-state inconsistency at %s: "
"using cache: %s, from scratch: %s")
- res1 res2))))
+ 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))
(when c-debug-parse-state
(c-toggle-parse-state-debug 1))
+\f
(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.
)))
\f
-;; Handling of small scale constructs like types and names.
+;; Setting and removing syntax properties on < and > in languages (C++
+;; and Java) where they can be template/generic delimiters as well as
+;; their normal meaning of "less/greater than".
+
+;; Normally, < and > have syntax 'punctuation'. When they are found to
+;; be delimiters, they are marked as such with the category properties
+;; c-<-as-paren-syntax, c->-as-paren-syntax respectively.
+
+;; STRATEGY:
+;;
+;; It is impossible to determine with certainty whether a <..> pair in
+;; C++ is two comparison operators or is template delimiters, unless
+;; one duplicates a lot of a C++ compiler. For example, the following
+;; code fragment:
+;;
+;; foo (a < b, c > d) ;
+;;
+;; could be a function call with two integer parameters (each a
+;; relational expression), or it could be a constructor for class foo
+;; taking one parameter d of templated type "a < b, c >". They are
+;; somewhat easier to distinguish in Java.
+;;
+;; The strategy now (2010-01) adopted is to mark and unmark < and
+;; > IN MATCHING PAIRS ONLY. [Previously, they were marked
+;; individually when their context so indicated. This gave rise to
+;; intractible problems when one of a matching pair was deleted, or
+;; pulled into a literal.]
+;;
+;; At each buffer change, the syntax-table properties are removed in a
+;; before-change function and reapplied, when needed, in an
+;; after-change function. It is far more important that the
+;; properties get removed when they they are spurious than that they
+;; be present when wanted.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun c-clear-<-pair-props (&optional pos)
+ ;; POS (default point) is at a < character. If it is marked with
+ ;; open paren syntax-table text property, remove the property,
+ ;; together with the close paren property on the matching > (if
+ ;; any).
+ (save-excursion
+ (if pos
+ (goto-char pos)
+ (setq pos (point)))
+ (when (equal (c-get-char-property (point) 'syntax-table)
+ c-<-as-paren-syntax)
+ (with-syntax-table c-no-parens-syntax-table ; ignore unbalanced [,{,(,..
+ (c-go-list-forward))
+ (when (equal (c-get-char-property (1- (point)) 'syntax-table)
+ c->-as-paren-syntax) ; should always be true.
+ (c-clear-char-property (1- (point)) 'category))
+ (c-clear-char-property pos 'category))))
+
+(defun c-clear->-pair-props (&optional pos)
+ ;; POS (default point) is at a > character. If it is marked with
+ ;; close paren syntax-table property, remove the property, together
+ ;; with the open paren property on the matching < (if any).
+ (save-excursion
+ (if pos
+ (goto-char pos)
+ (setq pos (point)))
+ (when (equal (c-get-char-property (point) 'syntax-table)
+ c->-as-paren-syntax)
+ (with-syntax-table c-no-parens-syntax-table ; ignore unbalanced [,{,(,..
+ (c-go-up-list-backward))
+ (when (equal (c-get-char-property (point) 'syntax-table)
+ c-<-as-paren-syntax) ; should always be true.
+ (c-clear-char-property (point) 'category))
+ (c-clear-char-property pos 'category))))
+
+(defun c-clear-<>-pair-props (&optional pos)
+ ;; POS (default point) is at a < or > character. If it has an
+ ;; open/close paren syntax-table property, remove this property both
+ ;; from the current character and its partner (which will also be
+ ;; thusly marked).
+ (cond
+ ((eq (char-after) ?\<)
+ (c-clear-<-pair-props pos))
+ ((eq (char-after) ?\>)
+ (c-clear->-pair-props pos))
+ (t (c-benign-error
+ "c-clear-<>-pair-props called from wrong position"))))
+
+(defun c-clear-<-pair-props-if-match-after (lim &optional pos)
+ ;; POS (default point) is at a < character. If it is both marked
+ ;; with open/close paren syntax-table property, and has a matching >
+ ;; (also marked) which is after LIM, remove the property both from
+ ;; the current > and its partner. Return t when this happens, nil
+ ;; when it doesn't.
+ (save-excursion
+ (if pos
+ (goto-char pos)
+ (setq pos (point)))
+ (when (equal (c-get-char-property (point) 'syntax-table)
+ c-<-as-paren-syntax)
+ (with-syntax-table c-no-parens-syntax-table ; ignore unbalanced [,{,(,..
+ (c-go-list-forward))
+ (when (and (>= (point) lim)
+ (equal (c-get-char-property (1- (point)) 'syntax-table)
+ c->-as-paren-syntax)) ; should always be true.
+ (c-unmark-<->-as-paren (1- (point)))
+ (c-unmark-<->-as-paren pos))
+ t)))
+
+(defun c-clear->-pair-props-if-match-before (lim &optional pos)
+ ;; POS (default point) is at a > character. If it is both marked
+ ;; with open/close paren syntax-table property, and has a matching <
+ ;; (also marked) which is before LIM, remove the property both from
+ ;; the current < and its partner. Return t when this happens, nil
+ ;; when it doesn't.
+ (save-excursion
+ (if pos
+ (goto-char pos)
+ (setq pos (point)))
+ (when (equal (c-get-char-property (point) 'syntax-table)
+ c->-as-paren-syntax)
+ (with-syntax-table c-no-parens-syntax-table ; ignore unbalanced [,{,(,..
+ (c-go-up-list-backward))
+ (when (and (<= (point) lim)
+ (equal (c-get-char-property (point) 'syntax-table)
+ c-<-as-paren-syntax)) ; should always be true.
+ (c-unmark-<->-as-paren (point))
+ (c-unmark-<->-as-paren pos))
+ t)))
+
+;; Set by c-common-init in cc-mode.el.
+(defvar c-new-BEG)
+(defvar c-new-END)
+
+(defun c-before-change-check-<>-operators (beg end)
+ ;; Unmark certain pairs of "< .... >" which are currently marked as
+ ;; template/generic delimiters. (This marking is via syntax-table
+ ;; text properties).
+ ;;
+ ;; These pairs are those which are in the current "statement" (i.e.,
+ ;; the region between the {, }, or ; before BEG and the one after
+ ;; END), and which enclose any part of the interval (BEG END).
+ ;;
+ ;; Note that in C++ (?and Java), template/generic parens cannot
+ ;; enclose a brace or semicolon, so we use these as bounds on the
+ ;; region we must work on.
+ ;;
+ ;; This function is called from before-change-functions (via
+ ;; c-get-state-before-change-functions). Thus the buffer is widened,
+ ;; and point is undefined, both at entry and exit.
+ ;;
+ ;; FIXME!!! This routine ignores the possibility of macros entirely.
+ ;; 2010-01-29.
+ (save-excursion
+ (let ((beg-lit-limits (progn (goto-char beg) (c-literal-limits)))
+ (end-lit-limits (progn (goto-char end) (c-literal-limits)))
+ new-beg new-end need-new-beg need-new-end)
+ ;; Locate the barrier before the changed region
+ (goto-char (if beg-lit-limits (car beg-lit-limits) beg))
+ (c-syntactic-skip-backward "^;{}" (max (- beg 2048) (point-min)))
+ (setq new-beg (point))
+
+ ;; Remove the syntax-table properties from each pertinent <...> pair.
+ ;; Firsly, the ones with the < before beg and > after beg.
+ (while (c-search-forward-char-property 'category 'c-<-as-paren-syntax beg)
+ (if (c-clear-<-pair-props-if-match-after beg (1- (point)))
+ (setq need-new-beg t)))
+
+ ;; Locate the barrier after END.
+ (goto-char (if end-lit-limits (cdr end-lit-limits) end))
+ (c-syntactic-re-search-forward "[;{}]"
+ (min (+ end 2048) (point-max)) 'end)
+ (setq new-end (point))
+
+ ;; Remove syntax-table properties from the remaining pertinent <...>
+ ;; pairs, those with a > after end and < before end.
+ (while (c-search-backward-char-property 'category 'c->-as-paren-syntax end)
+ (if (c-clear->-pair-props-if-match-before end)
+ (setq need-new-end t)))
+
+ ;; Extend the fontification region, if needed.
+ (when need-new-beg
+ (goto-char new-beg)
+ (c-forward-syntactic-ws)
+ (and (< (point) c-new-BEG) (setq c-new-BEG (point))))
+
+ (when need-new-end
+ (and (> new-end c-new-END) (setq c-new-END new-end))))))
+
+
(defun c-after-change-check-<>-operators (beg end)
;; This is called from `after-change-functions' when
(< beg (setq beg (match-end 0))))
(while (progn (skip-chars-forward "^<>" beg)
(< (point) beg))
- (c-clear-char-property (point) 'syntax-table)
+ (c-clear-<>-pair-props)
(forward-char))))
(when (< beg end)
(< end (setq end (match-end 0))))
(while (progn (skip-chars-forward "^<>" end)
(< (point) end))
- (c-clear-char-property (point) 'syntax-table)
+ (c-clear-<>-pair-props)
(forward-char)))))))
+
+\f
+;; Handling of small scale constructs like types and names.
+
;; Dynamically bound variable that instructs `c-forward-type' to also
;; treat possible types (i.e. those that it normally returns 'maybe or
;; 'found for) as actual types (and always return 'found for them).
(goto-char safe-pos)
t)))
+;; cc-mode requires cc-fonts.
+(declare-function c-fontify-recorded-types-and-refs "cc-fonts" ())
+
(defvar c-forward-<>-arglist-recur-depth)
(defun c-forward-<>-arglist (all-types)
;; `nconc' doesn't mind that the tail of
;; `c-record-found-types' is t.
(nconc c-record-found-types c-record-type-identifiers)))
+ (if (c-major-mode-is 'java-mode) (c-fontify-recorded-types-and-refs))
t)
(goto-char start)
;; List that collects the positions after the argument
;; separating ',' in the arglist.
arg-start-pos)
-
;; If the '<' has paren open syntax then we've marked it as an angle
;; bracket arglist before, so skip to the end.
(if (and (not c-parse-and-markup-<>-arglists)
(if (and (c-go-up-list-forward)
(eq (char-before) ?>))
t
-
;; Got unmatched paren angle brackets. We don't clear the paren
;; syntax properties and retry, on the basis that it's very
;; unlikely that paren angle brackets become operators by code
nil))
(forward-char)
+
(unless (looking-at c-<-op-cont-regexp)
(while (and
(progn
-
- (when c-record-type-identifiers
- (if all-types
-
- ;; All encountered identifiers are types, so set the
- ;; promote flag and parse the type.
- (progn
- (c-forward-syntactic-ws)
+ (c-forward-syntactic-ws)
+ (let ((orig-record-found-types c-record-found-types))
+ (when (or (and c-record-type-identifiers all-types)
+ (c-major-mode-is 'java-mode))
+ ;; All encountered identifiers are types, so set the
+ ;; promote flag and parse the type.
+ (progn
+ (c-forward-syntactic-ws)
+ (if (looking-at "\\?")
+ (forward-char)
(when (looking-at c-identifier-start)
- (let ((c-promote-possible-types t))
+ (let ((c-promote-possible-types t)
+ (c-record-found-types t))
(c-forward-type))))
- ;; Check if this arglist argument is a sole type. If
- ;; it's known then it's recorded in
- ;; `c-record-type-identifiers'. If it only is found
- ;; then it's recorded in `c-record-found-types' which we
- ;; might roll back if it turns out that this isn't an
- ;; angle bracket arglist afterall.
- (when (memq (char-before) '(?, ?<))
- (let ((orig-record-found-types c-record-found-types))
+ (c-forward-syntactic-ws)
+
+ (when (or (looking-at "extends")
+ (looking-at "super"))
+ (forward-word)
(c-forward-syntactic-ws)
- (and (memq (c-forward-type) '(known found))
- (not (looking-at "[,>]"))
- ;; A found type was recorded but it's not the
- ;; only thing in the arglist argument, so reset
- ;; `c-record-found-types'.
- (setq c-record-found-types
- orig-record-found-types))))))
+ (let ((c-promote-possible-types t)
+ (c-record-found-types t))
+ (c-forward-type)
+ (c-forward-syntactic-ws))))))
(setq pos (point))
- (or (when (eq (char-after) ?>)
- ;; Must check for '>' at the very start separately,
- ;; since the regexp below has to avoid ">>" without
- ;; using \\=.
- (forward-char)
- t)
-
- ;; Note: These regexps exploit the match order in \| so
- ;; that "<>" is matched by "<" rather than "[^>:-]>".
- (c-syntactic-re-search-forward
- (if c-restricted-<>-arglists
- ;; Stop on ',', '|', '&', '+' and '-' to catch
- ;; common binary operators that could be between
- ;; two comparison expressions "a<b" and "c>d".
- "[<;{},|&+-]\\|\\([^>:-]>\\)"
- ;; Otherwise we still stop on ',' to find the
- ;; argument start positions.
- "[<;{},]\\|\\([^>:-]>\\)")
- nil 'move t t 1)
-
- ;; If the arglist starter has lost its open paren
- ;; syntax but not the closer, we won't find the
- ;; closer above since we only search in the
- ;; balanced sexp. In that case we stop just short
- ;; of it so check if the following char is the closer.
- (when (eq (char-after) ?>)
- (forward-char)
- t)))
+
+ ;; Note: These regexps exploit the match order in \| so
+ ;; that "<>" is matched by "<" rather than "[^>:-]>".
+ (c-syntactic-re-search-forward
+ ;; Stop on ',', '|', '&', '+' and '-' to catch
+ ;; common binary operators that could be between
+ ;; two comparison expressions "a<b" and "c>d".
+ "[<;{},|+&-]\\|[>)]"
+ nil t t))
(cond
((eq (char-before) ?>)
((eq (char-before) ?<)
;; Either an operator starting with '<' or a nested arglist.
-
(setq pos (point))
(let (id-start id-end subres keyword-match)
(if (if (looking-at c-<-op-cont-regexp)
(setq id-start (point))))
(setq subres
- (let ((c-record-type-identifiers nil)
- (c-record-found-types nil))
+ (let ((c-promote-possible-types t)
+ (c-record-found-types t))
(c-forward-<>-arglist-recur
(and keyword-match
(c-keyword-member
(c-record-type-id (cons id-start id-end))))))
t)
- ((and (eq (char-before) ?,)
- (not c-restricted-<>-arglists))
- ;; Just another argument. Record the position. The
+ ((and (not c-restricted-<>-arglists)
+ (or (and (eq (char-before) ?&)
+ (not (eq (char-after) ?&)))
+ (eq (char-before) ?,)))
+ ;; Just another argument. Record the position. The
;; type check stuff that made us stop at it is at
;; the top of the loop.
(setq arg-start-pos (cons (point) arg-start-pos)))
;; it's useless to try to find a surrounding arglist
;; if we're nested.
(throw 'angle-bracket-arglist-escape nil))))))
-
(if res
(or c-record-found-types t)))))
(defun c-forward-name ()
;; Move forward over a complete name if at the beginning of one,
- ;; stopping at the next following token. If the point is not at
- ;; something that are recognized as name then it stays put. A name
- ;; could be something as simple as "foo" in C or something as
+ ;; stopping at the next following token. A keyword, as such,
+ ;; doesn't count as a name. If the point is not at something that
+ ;; is recognized as a name then it stays put.
+ ;;
+ ;; A name could be something as simple as "foo" in C or something as
;; complex as "X<Y<class A<int>::B, BIT_MAX >> b>, ::operator<> ::
;; Z<(a>b)> :: operator const X<&foo>::T Q::G<unsigned short
;; int>::*volatile const" in C++ (this function is actually little
;; more than a `looking-at' call in all modes except those that,
- ;; like C++, have `c-recognize-<>-arglists' set). Return nil if no
- ;; name is found, 'template if it's an identifier ending with an
- ;; angle bracket arglist, 'operator of it's an operator identifier,
- ;; or t if it's some other kind of name.
+ ;; like C++, have `c-recognize-<>-arglists' set).
+ ;;
+ ;; Return
+ ;; o - nil if no name is found;
+ ;; o - 'template if it's an identifier ending with an angle bracket
+ ;; arglist;
+ ;; o - 'operator of it's an operator identifier;
+ ;; o - t if it's some other kind of name.
;;
;; This function records identifier ranges on
;; `c-record-type-identifiers' and `c-record-ref-identifiers' if
((and c-recognize-<>-arglists
(eq (char-after) ?<))
;; Maybe an angle bracket arglist.
-
- (when (let (c-record-type-identifiers
- c-record-found-types)
+ (when (let ((c-record-type-identifiers t)
+ (c-record-found-types t))
(c-forward-<>-arglist nil))
(c-add-type start (1+ pos))
(goto-char pos)
res))
-(defun c-forward-type ()
+(defun c-forward-type (&optional brace-block-too)
;; Move forward over a type spec if at the beginning of one,
- ;; stopping at the next following token. Return t if it's a known
- ;; type that can't be a name or other expression, 'known if it's an
- ;; otherwise known type (according to `*-font-lock-extra-types'),
- ;; 'prefix if it's a known prefix of a type, 'found if it's a type
- ;; that matches one in `c-found-types', 'maybe if it's an identfier
- ;; that might be a type, or nil if it can't be a type (the point
- ;; isn't moved then). The point is assumed to be at the beginning
- ;; of a token.
+ ;; stopping at the next following token. The keyword "typedef"
+ ;; isn't part of a type spec here.
+ ;;
+ ;; BRACE-BLOCK-TOO, when non-nil, means move over the brace block in
+ ;; constructs like "struct foo {...} bar ;" or "struct {...} bar;".
+ ;; The current (2009-03-10) intention is to convert all uses of
+ ;; `c-forward-type' to call with this parameter set, then to
+ ;; eliminate it.
+ ;;
+ ;; Return
+ ;; o - t if it's a known type that can't be a name or other
+ ;; expression;
+ ;; o - 'known if it's an otherwise known type (according to
+ ;; `*-font-lock-extra-types');
+ ;; o - 'prefix if it's a known prefix of a type;
+ ;; o - 'found if it's a type that matches one in `c-found-types';
+ ;; o - 'maybe if it's an identfier that might be a type; or
+ ;; o - nil if it can't be a type (the point isn't moved then).
+ ;;
+ ;; The point is assumed to be at the beginning of a token.
;;
;; Note that this function doesn't skip past the brace definition
;; that might be considered part of the type, e.g.
;; `c-record-type-identifiers' is non-nil.
;;
;; This function might do hidden buffer changes.
+ (when (and c-recognize-<>-arglists
+ (looking-at "<"))
+ (c-forward-<>-arglist t)
+ (c-forward-syntactic-ws))
(let ((start (point)) pos res name-res id-start id-end id-range)
;; Skip leading type modifiers. If any are found we know it's a
;; prefix of a type.
- (when c-opt-type-modifier-key
+ (when c-opt-type-modifier-key ; e.g. "const" "volatile", but NOT "typedef"
(while (looking-at c-opt-type-modifier-key)
(goto-char (match-end 1))
(c-forward-syntactic-ws)
(setq res 'prefix)))
(cond
- ((looking-at c-type-prefix-key)
- ;; Looking at a keyword that prefixes a type identifier,
- ;; e.g. "class".
+ ((looking-at c-type-prefix-key) ; e.g. "struct", "class", but NOT
+ ; "typedef".
(goto-char (match-end 1))
(c-forward-syntactic-ws)
(setq pos (point))
- (if (memq (setq name-res (c-forward-name)) '(t template))
- (progn
- (when (eq name-res t)
- ;; In many languages the name can be used without the
- ;; prefix, so we add it to `c-found-types'.
- (c-add-type pos (point))
- (when (and c-record-type-identifiers
- c-last-identifier-range)
- (c-record-type-id c-last-identifier-range)))
- (setq res t))
- ;; Invalid syntax.
- (goto-char start)
- (setq res nil)))
+
+ (setq name-res (c-forward-name))
+ (setq res (not (null name-res)))
+ (when (eq name-res t)
+ ;; In many languages the name can be used without the
+ ;; prefix, so we add it to `c-found-types'.
+ (c-add-type pos (point))
+ (when (and c-record-type-identifiers
+ c-last-identifier-range)
+ (c-record-type-id c-last-identifier-range)))
+ (when (and brace-block-too
+ (memq res '(t nil))
+ (eq (char-after) ?\{)
+ (save-excursion
+ (c-safe
+ (progn (c-forward-sexp)
+ (c-forward-syntactic-ws)
+ (setq pos (point))))))
+ (goto-char pos)
+ (setq res t))
+ (unless res (goto-char start))) ; invalid syntax
((progn
(setq pos nil)
(setq res nil)))))
(when res
- ;; Skip trailing type modifiers. If any are found we know it's
+ ;; Skip trailing type modifiers. If any are found we know it's
;; a type.
(when c-opt-type-modifier-key
- (while (looking-at c-opt-type-modifier-key)
+ (while (looking-at c-opt-type-modifier-key) ; e.g. "const", "volatile"
(goto-char (match-end 1))
(c-forward-syntactic-ws)
(setq res t)))
-
;; Step over any type suffix operator. Do not let the existence
;; of these alter the classification of the found type, since
;; these operators typically are allowed in normal expressions
(goto-char (match-end 1))
(c-forward-syntactic-ws)))
- (when c-opt-type-concat-key
+ (when c-opt-type-concat-key ; Only/mainly for pike.
;; Look for a trailing operator that concatenates the type
;; with a following one, and if so step past that one through
;; a recursive call. Note that we don't record concatenated
res))
+(defun c-forward-annotation ()
+ ;; Used for Java code only at the moment. Assumes point is on the
+ ;; @, moves forward an annotation. returns nil if there is no
+ ;; annotation at point.
+ (and (looking-at "@")
+ (progn (forward-char) t)
+ (c-forward-type)
+ (progn (c-forward-syntactic-ws) t)
+ (if (looking-at "(")
+ (c-go-list-forward)
+ t)))
+
\f
;; Handling of large scale constructs like statements and declarations.
;; car ^ ^ point
;; Foo::Foo (int b) : Base (b) {}
;; car ^ ^ point
- ;;
- ;; The cdr of the return value is non-nil iff a `c-typedef-decl-kwds'
- ;; specifier (e.g. class, struct, enum, typedef) is found in the
- ;; declaration, i.e. the declared identifier(s) are types.
- ;;
+ ;;
+ ;; The cdr of the return value is non-nil when a
+ ;; `c-typedef-decl-kwds' specifier is found in the declaration.
+ ;; Specifically it is a dotted pair (A . B) where B is t when a
+ ;; `c-typedef-kwds' ("typedef") is present, and A is t when some
+ ;; other `c-typedef-decl-kwds' (e.g. class, struct, enum)
+ ;; specifier is present. I.e., (some of) the declared
+ ;; identifier(s) are types.
+ ;;
;; If a cast is parsed:
;;
;; The point is left at the first token after the closing paren of
;; If `backup-at-type' is nil then the other variables have
;; undefined values.
backup-at-type backup-type-start backup-id-start
- ;; Set if we've found a specifier that makes the defined
- ;; identifier(s) types.
+ ;; Set if we've found a specifier (apart from "typedef") that makes
+ ;; the defined identifier(s) types.
at-type-decl
+ ;; Set if we've a "typedef" keyword.
+ at-typedef
;; Set if we've found a specifier that can start a declaration
;; where there's no type.
maybe-typeless
(save-rec-type-ids c-record-type-identifiers)
(save-rec-ref-ids c-record-ref-identifiers))
+ (while (c-forward-annotation)
+ (c-forward-syntactic-ws))
+
;; Check for a type. Unknown symbols are treated as possible
;; types, but they could also be specifiers disguised through
;; macros like __INLINE__, so we recognize both types and known
;; Look for a specifier keyword clause.
(when (looking-at c-prefix-spec-kwds-re)
+ (if (looking-at c-typedef-key)
+ (setq at-typedef t))
(setq kwd-sym (c-keyword-sym (match-string 1)))
(save-excursion
(c-forward-keyword-clause 1)
(setq kwd-clause-end (point))))
- (when (setq found-type (c-forward-type))
+ (when (setq found-type (c-forward-type t)) ; brace-block-too
;; Found a known or possible type or a prefix of a known type.
(when at-type
(setq backup-maybe-typeless t)))
(when (c-keyword-member kwd-sym 'c-typedef-decl-kwds)
+ ;; This test only happens after we've scanned a type.
+ ;; So, with valid syntax, kwd-sym can't be 'typedef.
(setq at-type-decl t))
(when (c-keyword-member kwd-sym 'c-typeless-decl-kwds)
(setq maybe-typeless t))
;; CASE 3
(when (= (point) start)
;; Got a plain list of identifiers. If a colon follows it's
- ;; a valid label. Otherwise the last one probably is the
- ;; declared identifier and we should back up to the previous
- ;; type, providing it isn't a cast.
- (if (eq (char-after) ?:)
- ;; If we've found a specifier keyword then it's a
- ;; declaration regardless.
- (throw 'at-decl-or-cast (eq at-decl-or-cast t))
+ ;; a valid label. Otherwise the last one probably is the
+ ;; declared identifier and we should back up to the previous
+ ;; type, providing it isn't a cast.
+ (if (and (eq (char-after) ?:)
+ (not (c-major-mode-is 'java-mode)))
+ ;; If we've found a specifier keyword then it's a
+ ;; declaration regardless.
+ (throw 'at-decl-or-cast (eq at-decl-or-cast t))
(setq backup-if-not-cast t)
(throw 'at-decl-or-cast t)))
(goto-char type-start)
(c-forward-type))))
- (cons id-start at-type-decl))
+ (cons id-start
+ (and (or at-type-decl at-typedef)
+ (cons at-type-decl at-typedef))))
(t
;; False alarm. Restore the recorded ranges.
;;
;; This function might do hidden buffer changes.
- (let (special-brace-list)
+ (let (special-brace-list placeholder)
(goto-char indent-point)
(skip-chars-forward " \t")
(c-add-stmt-syntax 'func-decl-cont nil t
containing-sexp paren-state))
+ ;;CASE F: continued statement and the only preceding items are
+ ;;annotations.
+ ((and (c-major-mode-is 'java-mode)
+ (setq placeholder (point))
+ (c-beginning-of-statement-1)
+ (progn
+ (while (and (c-forward-annotation)
+ (< (point) placeholder))
+ (c-forward-syntactic-ws))
+ t)
+ (prog1
+ (>= (point) placeholder)
+ (goto-char placeholder)))
+ (c-beginning-of-statement-1 containing-sexp)
+ (c-add-syntax 'annotation-var-cont (point)))
+
;; CASE D: continued statement.
(t
(c-beginning-of-statement-1 containing-sexp)
(when (and containing-sexp
(eq (char-after containing-sexp) ?\())
(setq c-stmt-delim-chars c-stmt-delim-chars-with-comma))
-
;; cache char before and after indent point, and move point to
;; the most likely position to perform the majority of tests
(goto-char indent-point)
(c-add-syntax 'objc-method-args-cont placeholder))
;; CASE 5L: we are at the first argument of a template
- ;; arglist that begins on the previous line.
- ((and c-recognize-<>-arglists
- (eq (char-before) ?<)
- (setq placeholder (1- (point)))
- (not (and c-overloadable-operators-regexp
- (c-after-special-operator-id lim))))
- (c-beginning-of-statement-1 (c-safe-position (point) paren-state))
- (c-add-syntax 'template-args-cont (c-point 'boi) placeholder))
-
- ;; CASE 5Q: we are at a statement within a macro.
- (macro-start
- (c-beginning-of-statement-1 containing-sexp)
- (c-add-stmt-syntax 'statement nil t containing-sexp paren-state))
-
- ;; CASE 5M: we are at a topmost continuation line
- (t
- (c-beginning-of-statement-1 (c-safe-position (point) paren-state))
+ ;; arglist that begins on the previous line.
+ ((and c-recognize-<>-arglists
+ (eq (char-before) ?<)
+ (not (and c-overloadable-operators-regexp
+ (c-after-special-operator-id lim))))
+ (c-beginning-of-statement-1 (c-safe-position (point) paren-state))
+ (c-add-syntax 'template-args-cont (c-point 'boi)))
+
+ ;; CASE 5Q: we are at a statement within a macro.
+ (macro-start
+ (c-beginning-of-statement-1 containing-sexp)
+ (c-add-stmt-syntax 'statement nil t containing-sexp paren-state))
+
+ ;;CASE 5N: We are at a tompmost continuation line and the only
+ ;;preceding items are annotations.
+ ((and (c-major-mode-is 'java-mode)
+ (setq placeholder (point))
+ (c-beginning-of-statement-1)
+ (progn
+ (while (and (c-forward-annotation))
+ (c-forward-syntactic-ws))
+ t)
+ (prog1
+ (>= (point) placeholder)
+ (goto-char placeholder)))
+ (c-add-syntax 'annotation-top-cont (c-point 'boi)))
+
+ ;; CASE 5M: we are at a topmost continuation line
+ (t
+ (c-beginning-of-statement-1 (c-safe-position (point) paren-state))
(when (c-major-mode-is 'objc-mode)
(setq placeholder (point))
(while (and (c-forward-objc-directive)
(c-add-syntax 'topmost-intro-cont (c-point 'boi)))
))
- ;; (CASE 6 has been removed.)
- ;; CASE 19: line is an expression, not a statement, and is directly
- ;; contained by a template delimiter. Most likely, we are in a
- ;; template arglist within a statement. This case is based on CASE
- ;; 7. At some point in the future, we may wish to create more
- ;; syntactic symbols such as `template-intro',
- ;; `template-cont-nonempty', etc., and distinguish between them as we
- ;; do for `arglist-intro' etc. (2009-12-07).
- ((and c-recognize-<>-arglists
- (setq containing-< (c-up-list-backward indent-point containing-sexp))
- (eq (char-after containing-<) ?\<))
- (setq placeholder (c-point 'boi containing-<))
- (goto-char containing-sexp) ; Most nested Lbrace/Lparen (but not
- ; '<') before indent-point.
- (if (>= (point) placeholder)
- (progn
- (forward-char)
- (skip-chars-forward " \t"))
- (goto-char placeholder))
- (c-add-stmt-syntax 'template-args-cont (list containing-<) t
- (c-most-enclosing-brace c-state-cache (point))
- paren-state))
-
+ ;; (CASE 6 has been removed.)
- ;; CASE 7: line is an expression, not a statement. Most
- ;; likely we are either in a function prototype or a function
- ;; call argument list, or a template argument list.
- ((not (or (and c-special-brace-lists
- (save-excursion
- (goto-char containing-sexp)
- (c-looking-at-special-brace-list)))
- (eq (char-after containing-sexp) ?{)
- (eq (char-after containing-sexp) ?<)))
- (cond
+ ;; CASE 7: line is an expression, not a statement. Most
+ ;; likely we are either in a function prototype or a function
+ ;; call argument list
+ ((not (or (and c-special-brace-lists
+ (save-excursion
+ (goto-char containing-sexp)
+ (c-looking-at-special-brace-list)))
+ (eq (char-after containing-sexp) ?{)))
+ (cond
- ;; CASE 7A: we are looking at the arglist closing paren.
+ ;; CASE 7A: we are looking at the arglist closing paren.
;; C.f. case 7F.
((memq char-after-ip '(?\) ?\]))
(goto-char containing-sexp)
(skip-chars-forward " \t"))
(goto-char placeholder))
(c-add-stmt-syntax 'arglist-close (list containing-sexp) t
- (c-most-enclosing-brace paren-state (point))
- paren-state))
+ (c-most-enclosing-brace paren-state (point))
+ paren-state))
- ;; CASE 7B: Looking at the opening brace of an
- ;; in-expression block or brace list. C.f. cases 4, 16A
- ;; and 17E.
+ ;; CASE 19: line is an expression, not a statement, and is directly
+ ;; contained by a template delimiter. Most likely, we are in a
+ ;; template arglist within a statement. This case is based on CASE
+ ;; 7. At some point in the future, we may wish to create more
+ ;; syntactic symbols such as `template-intro',
+ ;; `template-cont-nonempty', etc., and distinguish between them as we
+ ;; do for `arglist-intro' etc. (2009-12-07).
+ ((and c-recognize-<>-arglists
+ (setq containing-< (c-up-list-backward indent-point containing-sexp))
+ (eq (char-after containing-<) ?\<))
+ (setq placeholder (c-point 'boi containing-<))
+ (goto-char containing-sexp) ; Most nested Lbrace/Lparen (but not
+ ; '<') before indent-point.
+ (if (>= (point) placeholder)
+ (progn
+ (forward-char)
+ (skip-chars-forward " \t"))
+ (goto-char placeholder))
+ (c-add-stmt-syntax 'template-args-cont (list containing-<) t
+ (c-most-enclosing-brace c-state-cache (point))
+ paren-state))
+
+ ;; CASE 7B: Looking at the opening brace of an
+ ;; in-expression block or brace list. C.f. cases 4, 16A
+ ;; and 17E.
((and (eq char-after-ip ?{)
(progn
(setq placeholder (c-inside-bracelist-p (point)
\f
(cc-provide 'cc-engine)
-;; arch-tag: 149add18-4673-4da5-ac47-6805e4eae089
;;; cc-engine.el ends here