(c-debug-remove-face ,beg ,end 'c-debug-decl-spot-face)
(c-debug-remove-face ,beg ,end 'c-debug-decl-sws-face))))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Machinery for determining when we're at top level (this including being
+;; directly inside a class or namespace, etc.)
+;;
+;; We maintain a stack of brace depths in structures like classes and
+;; namespaces. The car of this structure, when non-nil, indicates that the
+;; associated position is within a template (etc.) structure, and the value is
+;; the position where the (outermost) template ends. The other elements in
+;; the structure are stacked elements, one each for each enclosing "top level"
+;; structure.
+;;
+;; At the very outermost level, the value of the stack would be (nil 1), the
+;; "1" indicating an enclosure in a notional all-enclosing block. After
+;; passing a keyword such as "namespace", the value would become (nil 0 1).
+;; At this point, passing a semicolon would cause the 0 to be dropped from the
+;; stack (at any other time, a semicolon is ignored). Alternatively, on
+;; passing an opening brace, the stack would become (nil 1 1). Each opening
+;; brace passed causes the cadr to be incremented, and passing closing braces
+;; causes it to be decremented until it reaches 1. On passing a closing brace
+;; when the cadr of the stack is at 1, this causes it to be removed from the
+;; stack, the corresponding namespace (etc.) structure having been closed.
+;;
+;; There is a special stack value -1 which means the C++ colon operator
+;; introducing a list of inherited classes has just been parsed. The value
+;; persists on the stack until the next open brace or semicolon.
+;;
+;; When the car of the stack is non-nil, i.e. when we're in a template (etc.)
+;; structure, braces are not counted. The counting resumes only after passing
+;; the template's closing position, which is recorded in the car of the stack.
+;;
+;; The test for being at top level consists of the cadr being 0 or 1.
+;;
+;; The values of this stack throughout a buffer are cached in a simple linear
+;; cache, every 5000 characters.
+;;
+;; Note to maintainers: This cache mechanism is MUCH faster than recalculating
+;; the stack at every entry to `c-find-decl-spots' using `c-at-toplevel-p' or
+;; the like.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; The approximate interval at which we cache the value of the brace stack.
+(defconst c-bs-interval 5000)
+;; The list of cached values of the brace stack. Each value in the list is a
+;; cons of the position it is valid for and the value of the stack as
+;; described above.
+(defvar c-bs-cache nil)
+(make-variable-buffer-local 'c-bs-cache)
+;; The position of the buffer at and below which entries in `c-bs-cache' are
+;; valid.
+(defvar c-bs-cache-limit 1)
+(make-variable-buffer-local 'c-bs-cache-limit)
+;; The previous buffer position for which the brace stack value was
+;; determined.
+(defvar c-bs-prev-pos most-positive-fixnum)
+(make-variable-buffer-local 'c-bs-prev-pos)
+;; The value of the brace stack at `c-bs-prev-pos'.
+(defvar c-bs-prev-stack nil)
+(make-variable-buffer-local 'c-bs-prev-stack)
+
+(defun c-init-bs-cache ()
+ ;; Initialize the cache in `c-bs-cache' and related variables.
+ (setq c-bs-cache nil
+ c-bs-cache-limit 1
+ c-bs-prev-pos most-positive-fixnum
+ c-bs-prev-stack nil))
+
+(defun c-truncate-bs-cache (pos &rest _ignore)
+ ;; Truncate the upper bound of the cache `c-bs-cache' to POS, if it is
+ ;; higher than that position. This is called as either a before- or
+ ;; after-change-function.
+ (setq c-bs-cache-limit
+ (min c-bs-cache-limit pos)))
+
+(defun c-update-brace-stack (stack from to)
+ ;; Give a brace-stack which has the value STACK at position FROM, update it
+ ;; to it's value at position TO, where TO is after (or equal to) FROM.
+ ;; Return a cons of either TO (if it is outside a literal) and this new
+ ;; value, or of the next position after TO outside a literal and the new
+ ;; value.
+ (let (match kwd-sym (prev-match-pos 1)
+ (s (cdr stack))
+ (bound-<> (car stack))
+ )
+ (save-excursion
+ (cond
+ ((and bound-<> (<= to bound-<>))
+ (goto-char to)) ; Nothing to do.
+ (bound-<>
+ (goto-char bound-<>)
+ (setq bound-<> nil))
+ (t (goto-char from)))
+ (while (and (< (point) to)
+ (c-syntactic-re-search-forward
+ (if (<= (car s) 0)
+ c-brace-stack-thing-key
+ c-brace-stack-no-semi-key)
+ to 'after-literal)
+ (> (point) prev-match-pos)) ; prevent infinite loop.
+ (setq prev-match-pos (point))
+ (setq match (match-string-no-properties 1)
+ kwd-sym (c-keyword-sym match))
+ (cond
+ ((and (equal match "{")
+ (progn (backward-char)
+ (prog1 (looking-at "\\s(")
+ (forward-char))))
+ (setq s (if s
+ (cons (if (<= (car s) 0)
+ 1
+ (1+ (car s)))
+ (cdr s))
+ (list 1))))
+ ((and (equal match "}")
+ (progn (backward-char)
+ (prog1 (looking-at "\\s)")
+ (forward-char))))
+ (setq s
+ (cond
+ ((and s (> (car s) 1))
+ (cons (1- (car s)) (cdr s)))
+ ((and (cdr s) (eq (car s) 1))
+ (cdr s))
+ (t s))))
+ ((and (equal match "<")
+ (progn (backward-char)
+ (prog1 (looking-at "\\s(")
+ (forward-char))))
+ (backward-char)
+ (if (c-forward-<>-arglist nil) ; Should always work.
+ (when (> (point) to)
+ (setq bound-<> (point)))
+ (forward-char)))
+ ((and (equal match ":")
+ s
+ (eq (car s) 0))
+ (setq s (cons -1 (cdr s))))
+ ((and (equal match ",")
+ (eq (car s) -1))) ; at "," in "class foo : bar, ..."
+ ((member match '(";" "," ")"))
+ (when (and s (cdr s) (<= (car s) 0))
+ (setq s (cdr s))))
+ ((c-keyword-member kwd-sym 'c-flat-decl-block-kwds)
+ (push 0 s))))
+ (cons (point)
+ (cons bound-<> s)))))
+
+(defun c-brace-stack-at (here)
+ ;; Given a buffer position HERE, Return the value of the brace stack there.
+ (save-excursion
+ (save-restriction
+ (widen)
+ (let ((c c-bs-cache)
+ (can-use-prev (<= c-bs-prev-pos c-bs-cache-limit))
+ elt stack pos npos high-elt)
+ ;; Trim the cache to take account of buffer changes.
+ (while (and c
+ (> (caar c) c-bs-cache-limit))
+ (setq c (cdr c)))
+ (setq c-bs-cache c)
+
+ (while (and c
+ (> (caar c) here))
+ (setq high-elt (car c))
+ (setq c (cdr c)))
+ (setq pos (or (and c (caar c))
+ (point-min)))
+
+ (setq elt (if c
+ (car c)
+ (cons (point-min)
+ (cons nil (list 1)))))
+ (when (not high-elt)
+ (setq stack (cdr elt))
+ (while
+ ;; Add an element to `c-state-semi-nonlit-pos-cache' each iteration.
+ (<= (setq npos (+ pos c-bs-interval)) here)
+ (setq elt (c-update-brace-stack stack pos npos))
+ (setq npos (car elt))
+ (setq stack (cdr elt))
+ (unless (eq npos (point-max)) ; NPOS could be in a literal at EOB.
+ (setq c-bs-cache (cons elt c-bs-cache)))
+ (setq pos npos)))
+
+ (if (> pos c-bs-cache-limit)
+ (setq c-bs-cache-limit pos))
+
+ ;; Can we just use the previous value?
+ (if (and can-use-prev
+ (<= c-bs-prev-pos here)
+ (> c-bs-prev-pos (car elt)))
+ (setq pos c-bs-prev-pos
+ stack c-bs-prev-stack)
+ (setq pos (car elt)
+ stack (cdr elt)))
+ (if (> here c-bs-cache-limit)
+ (setq c-bs-cache-limit here))
+ (setq elt (c-update-brace-stack stack pos here)
+ c-bs-prev-pos (car elt)
+ c-bs-prev-stack (cdr elt))))))
+
+(defun c-bs-at-toplevel-p (here)
+ ;; Is position HERE at the top level, as indicated by the brace stack?
+ (let ((stack (c-brace-stack-at here)))
+ (or (null stack) ; Probably unnecessary.
+ (<= (cadr stack) 1))))
+
(defmacro c-find-decl-prefix-search ()
;; Macro used inside `c-find-decl-spots'. It ought to be a defun,
;; but it contains lots of free variables that refer to things
cfd-re-match nil)
(setq cfd-match-pos cfd-prop-match
cfd-prop-match nil))
+ (setq cfd-top-level (c-bs-at-toplevel-p cfd-match-pos))
(goto-char cfd-match-pos)
;; comments.
(cfd-token-pos 0)
;; The end position of the last entered macro.
- (cfd-macro-end 0))
+ (cfd-macro-end 0)
+ ;; Whether the last position returned from `c-find-decl-prefix-search'
+ ;; is at the top-level (including directly in a class or namespace,
+ ;; etc.).
+ cfd-top-level)
;; Initialize by finding a syntactically relevant start position
;; before the point, and do the first `c-decl-prefix-or-start-re'
nil)))) ; end of when condition
(c-debug-put-decl-spot-faces cfd-match-pos (point))
- (if (funcall cfd-fun cfd-match-pos (/= cfd-macro-end 0))
+ (if (funcall cfd-fun cfd-match-pos (/= cfd-macro-end 0) cfd-top-level)
(setq cfd-prop-match nil))
(when (/= cfd-macro-end 0)
;; Assuming point is at the start of a declarator, move forward over it,
;; leaving point at the next token after it (e.g. a ) or a ; or a ,).
;;
- ;; Return a list (ID-START ID-END BRACKETS-AFTER-ID GOT-INIT), where ID-START and
- ;; ID-END are the bounds of the declarator's identifier, and
- ;; BRACKETS-AFTER-ID is non-nil if a [...] pair is present after the id.
- ;; GOT-INIT is non-nil when the declarator is followed by "=" or "(".
+ ;; Return a list (ID-START ID-END BRACKETS-AFTER-ID GOT-INIT DECORATED),
+ ;; where ID-START and ID-END are the bounds of the declarator's identifier,
+ ;; and BRACKETS-AFTER-ID is non-nil if a [...] pair is present after the id.
+ ;; GOT-INIT is non-nil when the declarator is followed by "=" or "(",
+ ;; DECORATED is non-nil when the identifier is embellished by an operator,
+ ;; like "*x", or "(*x)".
;;
;; If ACCEPT-ANON is non-nil, move forward over any "anonymous declarator",
;; i.e. something like the (*) in int (*), such as might be found in a
;; array/struct initialization) or "=" or terminating delimiter
;; (e.g. "," or ";" or "}").
(let ((here (point))
- id-start id-end brackets-after-id paren-depth)
+ id-start id-end brackets-after-id paren-depth decorated)
(or limit (setq limit (point-max)))
(if (and
(< (point) limit)
(setq got-identifier t)
nil))
t))
+ (if (looking-at c-type-decl-operator-prefix-key)
+ (setq decorated t))
(if (eq (char-after) ?\()
(progn
(setq paren-depth (1+ paren-depth))
(setq brackets-after-id t))
(backward-char)
found))
- (list id-start id-end brackets-after-id (match-beginning 1))
+ (list id-start id-end brackets-after-id (match-beginning 1) decorated)
(goto-char here)
nil)))
;; inside a function declaration arglist).
;; '<> In an angle bracket arglist.
;; 'arglist Some other type of arglist.
+ ;; 'top Some other context and point is at the top-level (either
+ ;; outside any braces or directly inside a class or namespace,
+ ;; etc.)
;; nil Some other context or unknown context. Includes
;; within the parens of an if, for, ... construct.
;; 'not-decl This value is never supplied to this function. It
maybe-typeless
backup-maybe-typeless
(when c-recognize-typeless-decls
- (and (not context)
+ (and (memq context '(nil top))
;; Deal with C++11's "copy-initialization"
;; where we have <type>(<constant>), by
;; contrasting with a typeless
(setq at-decl-end
(looking-at (cond ((eq context '<>) "[,>]")
- (context "[,)]")
+ ((not (memq context '(nil top))) "[,\)]")
(t "[,;]"))))
;; Now we've collected info about various characteristics of
(if (and got-parens
(not got-prefix)
- (not context)
+ (memq context '(nil top))
(not (eq at-type t))
(or backup-at-type
maybe-typeless
;; instantiation expression).
(throw 'at-decl-or-cast nil))))
+ ;; CASE 9.5
+ (when (and (not context) ; i.e. not at top level.
+ (c-major-mode-is 'c++-mode)
+ (eq at-decl-or-cast 'ids)
+ after-paren-pos)
+ ;; We've got something like "foo bar (...)" in C++ which isn't at
+ ;; the top level. This is probably a uniform initialization of bar
+ ;; to the contents of the parens. In this case the declarator ends
+ ;; at the open paren.
+ (goto-char (1- after-paren-pos))
+ (throw 'at-decl-or-cast t))
+
;; CASE 10
(when at-decl-or-cast
;; By now we've located the type in the declaration that we know
;; CASE 11
(when (and got-identifier
- (not context)
+ (memq context '(nil top))
(looking-at c-after-suffixed-type-decl-key)
(if (and got-parens
(not got-prefix)
(when (and got-prefix-before-parens
at-type
(or at-decl-end (looking-at "=[^=]"))
- (not context)
+ (memq context '(nil top))
(or (not got-suffix)
at-decl-start))
;; Got something like "foo * bar;". Since we're not inside
(throw 'at-decl-or-cast t)))
;; CASE 18
- (when (and context
+ (when (and (not (memq context '(nil top)))
(or got-prefix
(and (eq context 'decl)
(not c-recognize-paren-inits)
(c-get-char-property (1- (point)) 'c-type)))))
(when (memq prop '(c-decl-id-start c-decl-type-start))
(c-forward-syntactic-ws limit)
- (c-font-lock-declarators limit t (eq prop 'c-decl-type-start))))
+ (c-font-lock-declarators limit t (eq prop 'c-decl-type-start)
+ (c-bs-at-toplevel-p (point)))))
(setq c-font-lock-context ;; (c-guess-font-lock-context)
(save-excursion
(goto-char pos)))))
nil)
-(defun c-font-lock-declarators (limit list types)
+(defun c-font-lock-declarators (limit list types not-top)
;; Assuming the point is at the start of a declarator in a declaration,
;; fontify the identifier it declares. (If TYPES is set, it does this via
;; the macro `c-fontify-types-and-refs'.)
;; additionally, mark the commas with c-type property 'c-decl-id-start or
;; 'c-decl-type-start (according to TYPES). Stop at LIMIT.
;;
- ;; If TYPES is non-nil, fontify all identifiers as types.
+ ;; If TYPES is non-nil, fontify all identifiers as types. If NOT-TOP is
+ ;; non-nil, we are not at the top-level ("top-level" includes being directly
+ ;; inside a class or namespace, etc.).
;;
;; Nil is always returned. The function leaves point at the delimiter after
;; the last declarator it processes.
(setq next-pos (point)
id-start (car decl-res)
id-face (if (and (eq (char-after) ?\()
+ (or (not (c-major-mode-is 'c++-mode))
+ (not not-top)
+ (car (cddr (cddr decl-res))) ; Id is in
+ ; parens, etc.
+ (save-excursion
+ (forward-char)
+ (c-forward-syntactic-ws)
+ (looking-at "[*&]")))
(not (car (cddr decl-res))) ; brackets-after-id
(or (not (c-major-mode-is 'c++-mode))
(save-excursion
c-decl-start-re
(eval c-maybe-decl-faces)
- (lambda (match-pos inside-macro)
+ (lambda (match-pos inside-macro &optional toplev)
;; Note to maintainers: don't use `limit' inside this lambda form;
;; c-find-decl-spots sometimes narrows to less than `limit'.
(setq start-pos (point))
(let ((type (and (> match-pos (point-min))
(c-get-char-property (1- match-pos) 'c-type))))
(cond ((not (memq (char-before match-pos) '(?\( ?, ?\[ ?< ?{)))
- (setq context nil
+ (setq context (and toplev 'top)
c-restricted-<>-arglists nil))
;; A control flow expression or a decltype
((and (eq (char-before match-pos) ?\()
'c-not-decl))
;; We're inside an "ordinary" open brace.
((eq (char-before match-pos) ?{)
- (setq context nil
+ (setq context (and toplev 'top)
c-restricted-<>-arglists nil))
;; Inside an angle bracket arglist.
((or (eq type 'c-<>-arg-sep)
(goto-char (car decl-or-cast))
(let ((decl-list
- (if context
+ (if (not (memq context '(nil top)))
;; Should normally not fontify a list of
;; declarators inside an arglist, but the first
;; argument in the ';' separated list of a "for"
'c-decl-id-start)))))
(c-font-lock-declarators
- (min limit (point-max)) decl-list (cadr decl-or-cast)))
+ (min limit (point-max)) decl-list
+ (cadr decl-or-cast) (not toplev)))
;; A declaration has been successfully identified, so do all the
;; fontification of types and refs that've been recorded.
(c-put-char-property (1- (point)) 'c-type 'c-decl-id-start)
(c-forward-syntactic-ws)
- (c-font-lock-declarators limit t nil)))
+ (c-font-lock-declarators limit t nil t)))
nil)
(defun c-font-lock-cut-off-declarators (limit)
;; fontification".
(let ((decl-search-lim (c-determine-limit 1000))
paren-state bod-res is-typedef encl-pos
+ (here (point))
c-recognize-knr-p) ; Strictly speaking, bogus, but it
; speeds up lisp.h tremendously.
(save-excursion
(c-forward-syntactic-ws))
;; At a real declaration?
(if (memq (c-forward-type t) '(t known found decltype))
- (c-font-lock-declarators limit t is-typedef)))))))
+ (c-font-lock-declarators
+ limit t is-typedef (not (c-bs-at-toplevel-p here)))))))))
nil))
(defun c-font-lock-enclosing-decls (limit)
(goto-char ps-elt)
(when (c-safe (c-forward-sexp))
(c-forward-syntactic-ws)
- (c-font-lock-declarators limit t in-typedef))))))))
+ (c-font-lock-declarators limit t in-typedef
+ (not (c-bs-at-toplevel-p (point)))))))))))
(defun c-font-lock-raw-strings (limit)
;; Fontify C++ raw strings.
(eval . (list ,(c-make-font-lock-search-function
'c-known-type-key
'(1 'font-lock-type-face t)
- '((c-font-lock-declarators limit t nil)
+ '((c-font-lock-declarators limit t nil nil)
(save-match-data
(goto-char (match-end 1))
(c-forward-syntactic-ws))
"\\)"))
`(,type-match
'font-lock-type-face t)
- `((c-font-lock-declarators limit t nil)
+ `((c-font-lock-declarators limit t nil nil)
(save-match-data
(goto-char (match-end ,type-match))
(c-forward-syntactic-ws))
(concat "\\<\\("
(regexp-opt (c-lang-const c-typeless-decl-kwds))
"\\)\\>")
- '((c-font-lock-declarators limit t nil)
+ '((c-font-lock-declarators limit t nil nil)
(save-match-data
(goto-char (match-end 1))
(c-forward-syntactic-ws))
;; before the '{' of the enum list, to avoid searching too far.
"[^][{};/#=]*"
"{")
- '((c-font-lock-declarators limit t nil)
+ '((c-font-lock-declarators limit t nil t)
(save-match-data
(goto-char (match-end 0))
(c-put-char-property (1- (point)) 'c-type
limit
"[-+]"
nil
- (lambda (match-pos inside-macro)
+ (lambda (match-pos inside-macro &optional top-level)
(forward-char)
(c-font-lock-objc-method))))
nil)
c-before-change-check-<>-operators
c-depropertize-CPP
c-before-after-change-digit-quote
- c-invalidate-macro-cache)
+ c-invalidate-macro-cache
+ c-truncate-bs-cache)
(c objc) '(c-extend-region-for-CPP
c-depropertize-CPP
- c-invalidate-macro-cache)
+ c-invalidate-macro-cache
+ c-truncate-bs-cache)
;; java 'c-before-change-check-<>-operators
awk 'c-awk-record-region-clear-NL)
(c-lang-defvar c-get-state-before-change-functions
(c-lang-defvar c-opt-inexpr-brace-list-key
(c-lang-const c-opt-inexpr-brace-list-key))
+(c-lang-defconst c-flat-decl-block-kwds
+ ;; Keywords that can introduce another declaration level, i.e. where a
+ ;; following "{" isn't a function block or brace list. Note that, for
+ ;; historical reasons, `c-decl-block-key' is NOT constructed from this lang
+ ;; const.
+ t (c--delete-duplicates
+ (append (c-lang-const c-class-decl-kwds)
+ (c-lang-const c-other-block-decl-kwds)
+ (c-lang-const c-inexpr-class-kwds))
+ :test 'string-equal))
+
+(c-lang-defconst c-brace-stack-thing-key
+ ;; Regexp matching any keyword or operator relevant to the brace stack (see
+ ;; `c-update-brace-stack' in cc-engine.el).
+ t (c-make-keywords-re 'appendable
+ (append
+ (c-lang-const c-flat-decl-block-kwds)
+ (if (c-lang-const c-recognize-<>-arglists)
+ '("{" "}" ";" "," ")" ":" "<")
+ '("{" "}" ";" "," ")" ":")))))
+(c-lang-defvar c-brace-stack-thing-key (c-lang-const c-brace-stack-thing-key))
+
+(c-lang-defconst c-brace-stack-no-semi-key
+ ;; Regexp matching any keyword or operator relevant to the brace stack when
+ ;; a semicolon is not relevant (see `c-update-brace-stack' in
+ ;; cc-engine.el).
+ t (c-make-keywords-re 'appendable
+ (append
+ (c-lang-const c-flat-decl-block-kwds)
+ (if (c-lang-const c-recognize-<>-arglists)
+ '("{" "}" "<")
+ '("{" "}")))))
+(c-lang-defvar c-brace-stack-no-semi-key
+ (c-lang-const c-brace-stack-no-semi-key))
+
(c-lang-defconst c-decl-block-key
;; Regexp matching keywords in any construct that contain another
;; declaration level, i.e. that isn't followed by a function block
(c-lang-defvar c-type-decl-prefix-key (c-lang-const c-type-decl-prefix-key)
'dont-doc)
+(c-lang-defconst c-type-decl-operator-prefix-key
+ "Regexp matching any declarator operator which isn't a keyword
+that might precede the identifier in a declaration, e.g. the
+\"*\" in \"char *argv\". The end of the first submatch is taken
+as the end of the operator. Identifier syntax is in effect when
+this is matched \(see `c-identifier-syntax-table')."
+ t ;; Default to a regexp that never matches.
+ "\\<\\>"
+ ;; Check that there's no "=" afterwards to avoid matching tokens
+ ;; like "*=".
+ (c objc) (concat "\\(\\*\\)"
+ "\\([^=]\\|$\\)")
+ c++ (concat "\\("
+ "\\.\\.\\."
+ "\\|"
+ "\\*"
+ "\\)"
+ "\\([^=]\\|$\\)")
+ pike "\\(\\*\\)\\([^=]\\|$\\)")
+(c-lang-defvar c-type-decl-operator-prefix-key
+ (c-lang-const c-type-decl-operator-prefix-key))
+
(c-lang-defconst c-type-decl-suffix-key
"Regexp matching the declarator operators that might follow after the
identifier in a declaration, e.g. the \"[\" in \"char argv[]\". This