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.
	(cond
	 ((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))
	 ((save-excursion (c-beginning-of-macro))
	  ;; The `c-backward-syntactic-ws' ~40 lines up failed to find non
	  ;; syntactic-ws and hit its limit, leaving us in a macro.
	  (setq cfd-match-pos cfd-start-pos
		cfd-continue-pos cfd-start-pos))
	 (t
	  (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,