Function: c-find-decl-spots
c-find-decl-spots is a byte-compiled function defined in
cc-engine.el.gz.
Signature
(c-find-decl-spots CFD-LIMIT CFD-DECL-RE CFD-FACE-CHECKLIST CFD-FUN)
Source Code
;; Defined in /usr/src/emacs/lisp/progmodes/cc-engine.el.gz
(defun c-find-decl-spots (cfd-limit cfd-decl-re cfd-face-checklist cfd-fun)
;; Call CFD-FUN for each possible spot for a declaration, cast or
;; label from the point to CFD-LIMIT.
;;
;; CFD-FUN is called with point at the start of the spot. It's passed three
;; arguments: The first is the end position of the token preceding the spot,
;; or 0 for the implicit match at bob. The second is a flag that is t when
;; the match is inside a macro. The third is a flag that is t when the
;; match is at "top level", i.e. outside any brace block, or directly inside
;; a class or namespace, etc. Point should be moved forward by at least one
;; token.
;;
;; If CFD-FUN adds `c-decl-end' properties somewhere below the current spot,
;; it should return non-nil to ensure that the next search will find them.
;;
;; Such a spot is:
;; o The first token after bob.
;; o The first token after the end of submatch 1 in
;; `c-decl-prefix-or-start-re' when that submatch matches. This
;; submatch is typically a (L or R) brace or paren, a ;, or a ,.
;; As a special case, noise macros are skipped over and the next
;; token regarded as the spot.
;; o The start of each `c-decl-prefix-or-start-re' match when
;; submatch 1 doesn't match. This is, for example, the keyword
;; "class" in Pike.
;; o The start of a previously recognized declaration; "recognized"
;; means that the last char of the previous token has a `c-type'
;; text property with the value `c-decl-end'; this only holds
;; when `c-type-decl-end-used' is set.
;;
;; Only a spot that match CFD-DECL-RE and whose face is in the
;; CFD-FACE-CHECKLIST list causes CFD-FUN to be called. The face
;; check is disabled if CFD-FACE-CHECKLIST is nil.
;;
;; If the match is inside a macro then the buffer is narrowed to the
;; end of it, so that CFD-FUN can investigate the following tokens
;; without matching something that begins inside a macro and ends
;; outside it. It's to avoid this work that the CFD-DECL-RE and
;; CFD-FACE-CHECKLIST checks exist.
;;
;; The spots are visited approximately in order from top to bottom.
;; It's however the positions where `c-decl-prefix-or-start-re'
;; matches and where `c-decl-end' properties are found that are in
;; order. Since the spots often are at the following token, they
;; might be visited out of order insofar as more spots are reported
;; later on within the syntactic whitespace between the match
;; positions and their spots.
;;
;; It's assumed that comments and strings are fontified in the
;; searched range.
;;
;; This is mainly used in fontification, and so has an elaborate
;; cache to handle repeated calls from the same start position; see
;; the variables above.
;;
;; All variables in this function begin with `cfd-' to avoid name
;; collision with the (dynamically bound) variables used in CFD-FUN.
;;
;; This function might do hidden buffer changes.
(let ((cfd-start-pos (point)) ; never changed
(cfd-buffer-end (point-max))
;; The end of the token preceding the decl spot last found
;; with `c-decl-prefix-or-start-re'. `cfd-limit' if there's
;; no match.
cfd-re-match
;; The end position of the last `c-decl-prefix-or-start-re'
;; match. If this is greater than `cfd-continue-pos', the
;; next regexp search is started here instead.
(cfd-re-match-end (point-min))
;; The end of the last `c-decl-end' found by
;; `c-find-decl-prefix-search'. `cfd-limit' if there's no
;; match. If searching for the property isn't needed then we
;; disable it by setting it to `cfd-limit' directly.
(cfd-prop-match (unless c-type-decl-end-used cfd-limit))
;; The end of the token preceding the decl spot last found by
;; `c-find-decl-prefix-search'. 0 for the implicit match at
;; bob. `cfd-limit' if there's no match. In other words,
;; this is the minimum of `cfd-re-match' and `cfd-prop-match'.
(cfd-match-pos cfd-limit)
;; The position to continue searching at.
cfd-continue-pos
;; The position of the last "real" token we've stopped at.
;; This can be greater than `cfd-continue-pos' when we get
;; hits inside macros or at `c-decl-end' positions inside
;; comments.
(cfd-token-pos 0)
;; The end position of the last entered macro.
(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 (c-bs-at-toplevel-p (point))))
;; Initialize by finding a syntactically relevant start position
;; before the point, and do the first `c-decl-prefix-or-start-re'
;; search unless we're at bob.
(let (start-in-literal start-in-macro syntactic-pos hash-define-pos)
;; Must back up a bit since we look for the end of the previous
;; statement or declaration, which is earlier than the first
;; returned match.
;; This `cond' moves back over any literals or macros. It has special
;; handling for when the region being searched is entirely within a
;; macro. It sets `cfd-continue-pos' (unless we've reached
;; `cfd-limit').
(cond
;; First we need to move to a syntactically relevant position.
;; Begin by backing out of comment or string literals.
;;
;; This arm of the cond actually triggers if we're in a literal,
;; and cfd-limit is at most at BONL.
((and
;; This arm of the `and' moves backwards out of a literal when
;; the face at point is a literal face. In this case, its value
;; is always non-nil.
(when (c-got-face-at (point) c-literal-faces)
;; Try to use the faces to back up to the start of the
;; literal. FIXME: What if the point is on a declaration
;; inside a comment?
(while (and (not (bobp))
(c-got-face-at (1- (point)) c-literal-faces))
(goto-char (previous-single-property-change
(point) 'face nil (point-min)))) ; No limit. FIXME, perhaps? 2020-12-07.
;; XEmacs doesn't fontify the quotes surrounding string
;; literals.
(and (featurep 'xemacs)
(eq (get-text-property (point) 'face)
'font-lock-string-face)
(not (bobp))
(progn (backward-char)
(not (looking-at c-string-limit-regexp)))
(forward-char))
;; Don't trust the literal to contain only literal faces
;; (the font lock package might not have fontified the
;; start of it at all, for instance) so check that we have
;; arrived at something that looks like a start or else
;; resort to `c-literal-limits'.
(unless (looking-at c-literal-start-regexp)
(let ((lit-start (c-literal-start)))
(if lit-start (goto-char lit-start)))
)
(setq start-in-literal (point))) ; end of `and' arm.
;; The start is in a literal. If the limit is in the same
;; one we don't have to find a syntactic position etc. We
;; only check that if the limit is at or before bonl to save
;; time; it covers the by far most common case when font-lock
;; refontifies the current line only.
(<= cfd-limit (c-point 'bonl cfd-start-pos))
(save-excursion
(goto-char cfd-start-pos)
(while (progn
(goto-char (c-next-single-property-change
(point) 'face nil cfd-limit))
(and (< (point) cfd-limit)
(c-got-face-at (point) c-literal-faces))))
(= (point) cfd-limit))) ; end of `cond' arm condition
;; Completely inside a literal. Set up variables to trig the
;; (< cfd-continue-pos cfd-start-pos) case below and it'll
;; find a suitable start position.
(setq cfd-continue-pos start-in-literal)) ; end of `cond' arm
;; Check if the region might be completely inside a macro, to
;; optimize that like the completely-inside-literal above.
((save-excursion
(and (= (forward-line 1) 0)
(bolp) ; forward-line has funny behavior at eob.
(>= (point) cfd-limit)
(progn (backward-char)
(eq (char-before) ?\\))))
;; (Maybe) completely inside a macro. Only need to trig the
;; (< cfd-continue-pos cfd-start-pos) case below to make it
;; set things up.
(setq cfd-continue-pos (1- cfd-start-pos)
start-in-macro t))
;; The default arm of the `cond' moves back over any macro we're in
;; and over any syntactic WS. It sets `c-find-decl-syntactic-pos'.
(t
;; Back out of any macro so we don't miss any declaration
;; that could follow after it.
(when (c-beginning-of-macro)
(setq start-in-macro t))
;; Now we're at a proper syntactically relevant position so we
;; can use the cache. But first clear it if it applied
;; further down.
(c-invalidate-find-decl-cache cfd-start-pos)
(setq syntactic-pos (point))
(unless
(eq syntactic-pos c-find-decl-syntactic-pos)
;; Don't have to do this if the cache is relevant here,
;; typically if the same line is refontified again. If
;; we're just some syntactic whitespace further down we can
;; still use the cache to limit the skipping.
(c-backward-syntactic-ws
(max (or c-find-decl-syntactic-pos (point-min))
(- (point) 10000) (point-min))))
;; If we hit `c-find-decl-syntactic-pos' and
;; `c-find-decl-match-pos' is set then we install the cached
;; values. If we hit `c-find-decl-syntactic-pos' and
;; `c-find-decl-match-pos' is nil then we know there's no decl
;; prefix in the whitespace before `c-find-decl-syntactic-pos'
;; and so we can continue the search from this point. If we
;; didn't hit `c-find-decl-syntactic-pos' then we're now in
;; the right spot to begin searching anyway.
(if (and (eq (point) c-find-decl-syntactic-pos)
c-find-decl-match-pos)
(setq cfd-match-pos c-find-decl-match-pos
cfd-continue-pos syntactic-pos)
(setq c-find-decl-syntactic-pos syntactic-pos)
(when (if (bobp)
;; Always consider bob a match to get the first
;; declaration in the file. Do this separately instead of
;; letting `c-decl-prefix-or-start-re' match bob, so that
;; regexp always can consume at least one character to
;; ensure that we won't get stuck in an infinite loop.
(setq cfd-re-match 0)
(backward-char)
(c-beginning-of-current-token)
(< (point) cfd-limit))
;; Do an initial search now. In the bob case above it's
;; only done to search for a `c-decl-end' spot.
(c-find-decl-prefix-search)) ; sets cfd-continue-pos
(setq c-find-decl-match-pos (and (< cfd-match-pos cfd-start-pos)
cfd-match-pos))))) ; end of `cond'
;; Advance `cfd-continue-pos' if it's before the start position.
;; The closest continue position that might have effect at or
;; after the start depends on what we started in. This also
;; finds a suitable start position in the special cases when the
;; region is completely within a literal or macro.
(when (and cfd-continue-pos (< cfd-continue-pos cfd-start-pos))
(cond
(start-in-macro
;; If we're in a macro then it's the closest preceding token
;; in the macro. Check this before `start-in-literal',
;; since if we're inside a literal in a macro, the preceding
;; token is earlier than any `c-decl-end' spot inside the
;; literal (comment).
(goto-char (or start-in-literal cfd-start-pos))
;; The only syntactic ws in macros are comments.
(c-backward-comments)
(or (bobp) (backward-char))
(c-beginning-of-current-token)
;; If we're in a macro without argument parentheses, we could have
;; now ended up at the macro's identifier. We need to be at #define
;; for `c-find-decl-prefix-search' to find the first token of the
;; macro's expansion.
(when (and (c-on-identifier)
(setq hash-define-pos
(save-excursion
(and
(zerop (c-backward-token-2 2)) ; over define, #
(save-excursion
(beginning-of-line)
(looking-at c-opt-cpp-macro-define-id))
(point)))))
(goto-char hash-define-pos)))
(start-in-literal
;; If we're in a comment it can only be the closest
;; preceding `c-decl-end' position within that comment, if
;; any. Go back to the beginning of such a property so that
;; `c-find-decl-prefix-search' will find the end of it.
;; (Can't stop at the end and install it directly on
;; `cfd-prop-match' since that variable might be cleared
;; after `cfd-fun' below.)
;;
;; Note that if the literal is a string then the property
;; search will simply skip to the beginning of it right
;; away.
(if (not c-type-decl-end-used)
(goto-char start-in-literal)
(goto-char cfd-start-pos)
(while (progn
(goto-char (previous-single-property-change
(point) 'c-type nil start-in-literal))
(and (> (point) start-in-literal)
(not (eq (c-get-char-property (point) 'c-type)
'c-decl-end))))))
(when (and (= (point) start-in-literal)
(not (looking-at c-doc-bright-comment-start-re)))
;; Didn't find any property inside the comment, so we can
;; skip it entirely. (This won't skip past a string, but
;; that'll be handled quickly by the next
;; `c-find-decl-prefix-search' anyway.)
(c-forward-single-comment)
(if (> (point) cfd-limit)
(goto-char cfd-limit))))
(t
;; If we started in normal code, the only match that might
;; apply before the start is what we already got in
;; `cfd-match-pos' so we can continue at the start position.
;; (Note that we don't get here if the first match is below
;; it.)
(goto-char cfd-start-pos))) ; end of `cond'
;; Delete found matches if they are before our new continue
;; position, so that `c-find-decl-prefix-search' won't back up
;; to them later on.
(setq cfd-continue-pos (point))
(when (and cfd-re-match (< cfd-re-match cfd-continue-pos))
(setq cfd-re-match nil))
(when (and cfd-prop-match (< cfd-prop-match cfd-continue-pos))
(setq cfd-prop-match nil))) ; end of `when'
(if syntactic-pos
;; This is the normal case and we got a proper syntactic
;; position. If there's a match then it's always outside
;; macros and comments, so advance to the next token and set
;; `cfd-token-pos'. The loop below will later go back using
;; `cfd-continue-pos' to fix declarations inside the
;; syntactic ws.
(when (and cfd-match-pos (< cfd-match-pos syntactic-pos))
(goto-char syntactic-pos)
(c-forward-syntactic-ws cfd-limit)
(and cfd-continue-pos
(< cfd-continue-pos (point))
(setq cfd-token-pos (point))))
;; Have one of the special cases when the region is completely
;; within a literal or macro. `cfd-continue-pos' is set to a
;; good start position for the search, so do it.
(c-find-decl-prefix-search)))
;; Now loop, one decl spot per iteration. We already have the first
;; match in `cfd-match-pos'.
(while (progn
;; Go forward over "false matches", one per iteration.
(while (and
(< cfd-match-pos cfd-limit)
(or
;; Kludge to filter out matches on the "<" that
;; aren't open parens, for the sake of languages
;; that got `c-recognize-<>-arglists' set.
(and (eq (char-before cfd-match-pos) ?<)
(not (c-get-char-property (1- cfd-match-pos)
'syntax-table)))
;; If `cfd-continue-pos' is less or equal to
;; `cfd-token-pos', we've got a hit inside a macro
;; that's in the syntactic whitespace before the last
;; "real" declaration we've checked. If they're equal
;; we've arrived at the declaration a second time, so
;; there's nothing to do.
(= cfd-continue-pos cfd-token-pos)
(progn
;; If `cfd-continue-pos' is less than `cfd-token-pos'
;; we're still searching for declarations embedded in
;; the syntactic whitespace. In that case we need
;; only to skip comments and not macros, since they
;; can't be nested, and that's already been done in
;; `c-find-decl-prefix-search'.
(when (> cfd-continue-pos cfd-token-pos)
(c-forward-syntactic-ws cfd-limit)
(setq cfd-token-pos (point)))
;; Continue if the following token fails the
;; CFD-DECL-RE and CFD-FACE-CHECKLIST checks.
(when (or (>= (point) cfd-limit)
(not (looking-at cfd-decl-re))
(and cfd-face-checklist
(not (c-got-face-at
(point) cfd-face-checklist))))
(goto-char cfd-continue-pos)
t)))
(< (point) cfd-limit)) ; end of "false matches" condition
(c-find-decl-prefix-search)) ; end of "false matches" loop
(< (point) cfd-limit)) ; end of condition for "decl-spot" while
(when (and
(>= (point) cfd-start-pos)
(progn
;; Narrow to the end of the macro if we got a hit inside
;; one, to avoid recognizing things that start inside the
;; macro and end outside it.
(when (> cfd-match-pos cfd-macro-end)
;; Not in the same macro as in the previous round.
(save-excursion
(goto-char cfd-match-pos)
(setq cfd-macro-end
(if (save-excursion (and (c-beginning-of-macro)
(< (point) cfd-match-pos)))
(progn (c-end-of-macro)
(point))
0))))
(if (zerop cfd-macro-end)
t
(if (> cfd-macro-end (point))
(progn (narrow-to-region (point-min) cfd-macro-end)
t)
;; The matched token was the last thing in the macro,
;; so the whole match is bogus.
(setq cfd-macro-end 0)
nil)))) ; end of when condition
(when (> cfd-macro-end 0)
(setq cfd-top-level nil)) ; In a macro is "never" at top level.
(c-debug-put-decl-spot-faces cfd-match-pos (point))
(if (funcall cfd-fun cfd-match-pos (/= cfd-macro-end 0) cfd-top-level)
(setq cfd-prop-match nil))
(when (/= cfd-macro-end 0)
;; Restore limits if we did macro narrowing above.
(narrow-to-region (point-min) cfd-buffer-end)))
(goto-char cfd-continue-pos)
(if (= cfd-continue-pos cfd-limit)
(setq cfd-match-pos cfd-limit)
(c-find-decl-prefix-search))))) ; Moves point, sets cfd-continue-pos,