Mercurial > emacs
diff lisp/progmodes/cc-engine.el @ 44728:7a3ac6c387fe
CC Mode update to version 5.29. This is for testing; it's not a released
version.
author | Martin Stjernholm <mast@lysator.liu.se> |
---|---|
date | Mon, 22 Apr 2002 00:35:36 +0000 |
parents | 4733cde26cbb |
children | 11c11cb2b8a8 |
line wrap: on
line diff
--- a/lisp/progmodes/cc-engine.el Sun Apr 21 18:21:47 2002 +0000 +++ b/lisp/progmodes/cc-engine.el Mon Apr 22 00:35:36 2002 +0000 @@ -25,12 +25,22 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this program; see the file COPYING. If not, write to +;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: +;; The functions which have docstring documentation can be considered +;; part of an API which other packages can use in CC Mode buffers. +;; Otoh, undocumented functions and functions with the documentation +;; in comments are considered purely internal and can change semantics +;; or even disappear in the future. +;; +;; (This policy applies to CC Mode as a whole, not just this file. It +;; probably also applies to many other Emacs packages, but here it's +;; clearly spelled out.) + ;;; Code: (eval-when-compile @@ -49,8 +59,18 @@ (cc-bytecomp-defun buffer-syntactic-context) ; XEmacs -(defvar c-state-cache nil) +(defun c-calculate-state (arg prevstate) + ;; Calculate the new state of PREVSTATE, t or nil, based on arg. If + ;; arg is nil or zero, toggle the state. If arg is negative, turn + ;; the state off, and if arg is positive, turn the state on + (if (or (not arg) + (zerop (setq arg (prefix-numeric-value arg)))) + (not prevstate) + (> arg 0))) + + (defvar c-in-literal-cache t) +(defvar c-parsing-error nil) ;; KLUDGE ALERT: c-maybe-labelp is used to pass information between ;; c-crosses-statement-barrier-p and c-beginning-of-statement-1. A @@ -58,285 +78,657 @@ ;; the byte compiler. (defvar c-maybe-labelp nil) -;; WARNING WARNING WARNING -;; -;; Be *exceptionally* careful about modifications to this function! -;; Much of CC Mode depends on this Doing The Right Thing. If you -;; break it you will be sorry. If you think you know how this works, -;; you probably don't. No human on Earth does! :-) -;; -;; WARNING WARNING WARNING +;; Macros used internally in c-beginning-of-statement-1 for the +;; automaton actions. +(defmacro c-bos-push-state () + '(setq stack (cons (cons state saved-pos) + stack))) +(defmacro c-bos-pop-state (&optional do-if-done) + `(if (setq state (car (car stack)) + saved-pos (cdr (car stack)) + stack (cdr stack)) + t + ,do-if-done + (throw 'loop nil))) +(defmacro c-bos-pop-state-and-retry () + '(throw 'loop (setq state (car (car stack)) + saved-pos (cdr (car stack)) + ;; Throw nil if stack is empty, else throw non-nil. + stack (cdr stack)))) +(defmacro c-bos-save-pos () + '(setq saved-pos (vector pos tok ptok pptok))) +(defmacro c-bos-restore-pos () + '(unless (eq (elt saved-pos 0) start) + (setq pos (elt saved-pos 0) + tok (elt saved-pos 1) + ptok (elt saved-pos 2) + pptok (elt saved-pos 3)) + (goto-char pos) + (setq sym nil))) +(defmacro c-bos-save-error-info (missing got) + `(setq saved-pos (vector pos ,missing ,got))) +(defmacro c-bos-report-error () + '(unless noerror + (setq c-parsing-error + (format "No matching `%s' found for `%s' on line %d" + (elt saved-pos 1) + (elt saved-pos 2) + (1+ (count-lines (point-min) + (c-point 'bol (elt saved-pos 0)))))))) + +(defun c-beginning-of-statement-1 (&optional lim ignore-labels + noerror comma-delim) + "Move to the start of the current statement or declaration, or to +the previous one if already at the beginning of one. Only +statements/declarations on the same level are considered, i.e. don't +move into or out of sexps (not even normal expression parentheses). + +Stop at statement continuations like \"else\", \"catch\", \"finally\" +and the \"while\" in \"do ... while\" if the start point is within +them. If starting at such a continuation, move to the corresponding +statement start. If at the beginning of a statement, move to the +closest containing statement if there is any. This might also stop at +a continuation clause. + +Labels are treated as separate statements if IGNORE-LABELS is non-nil. +The function is not overly intelligent in telling labels from other +uses of colons; if used outside a statement context it might trip up +on e.g. inherit colons, so IGNORE-LABELS should be used then. There +should be no such mistakes in a statement context, however. + +Macros are ignored unless point is within one, in which case the +content of the macro is treated as normal code. Aside from any normal +statement starts found in it, stop at the first token of the content +in the macro, i.e. the expression of an \"#if\" or the start of the +definition in a \"#define\". Also stop at start of macros before +leaving them. + +Return 'label if stopped at a label, 'same if stopped at the beginning +of the current statement, 'up if stepped to a containing statement, +'previous if stepped to a preceding statement, 'beginning if stepped +from a statement continuation clause to its start clause, or 'macro if +stepped to a macro start. Note that 'same and not 'label is returned +if stopped at the same label without crossing the colon character. + +LIM may be given to limit the search. If the search hits the limit, +point will be left at the closest following token, or at the start +position if that is less ('same is returned in this case). + +NOERROR turns off error logging to `c-parsing-error'. + +Normally only ';' is considered to delimit statements, but if +COMMA-DELIM is non-nil then ',' is treated likewise." -(defun c-beginning-of-statement-1 (&optional lim) - ;; move to the start of the current statement, or the previous - ;; statement if already at the beginning of one. - (let ((firstp t) - (substmt-p t) - donep c-in-literal-cache saved - (last-begin (point))) - ;; first check for bare semicolon - (if (and (progn (c-backward-syntactic-ws lim) - (eq (char-before) ?\;)) - (c-safe (progn (forward-char -1) - (setq saved (point)) - t)) - (progn (c-backward-syntactic-ws lim) - (memq (char-before) '(?\; ?{ ?:))) - ) - (setq last-begin saved) - (goto-char last-begin) - (while (not donep) - ;; stop at beginning of buffer - (if (bobp) (setq donep t) - ;; go backwards one balanced expression, but be careful of - ;; unbalanced paren being reached - (if (not (c-safe (progn (c-backward-sexp 1) t))) - (progn - (if firstp - (backward-up-list 1) - (goto-char last-begin)) - ;; skip over any unary operators, or other special - ;; characters appearing at front of identifier - (save-excursion - (c-backward-syntactic-ws lim) - (skip-chars-backward "-+!*&:.~@ \t\n") - (if (eq (char-before) ?\() - (setq last-begin (point)))) - (goto-char last-begin) - (setq donep t))) + ;; The bulk of this function is a pushdown automaton that looks at + ;; statement boundaries and the tokens in c-opt-block-stmt-key. + ;; + ;; Note: The position of a boundary is the following token. + ;; + ;; Begin with current token, stop when stack is empty and the + ;; position has been moved. + ;; + ;; Common state: + ;; "else": Push state, goto state `else': + ;; boundary: Goto state `else-boundary': + ;; "if": Pop state. + ;; boundary: Error, pop state. + ;; other: See common state. + ;; other: Error, pop state, retry token. + ;; "while": Push state, goto state `while': + ;; boundary: Save position, goto state `while-boundary': + ;; "do": Pop state. + ;; boundary: Restore position if it's not at start, pop state. + ;; other: See common state. + ;; other: Pop state, retry token. + ;; "catch" or "finally": Push state, goto state `catch': + ;; boundary: Goto state `catch-boundary': + ;; "try": Pop state. + ;; "catch": Goto state `catch'. + ;; boundary: Error, pop state. + ;; other: See common state. + ;; other: Error, pop state, retry token. + ;; other: Do nothing special. + ;; + ;; In addition to the above there is some special handling of labels + ;; and macros. + + (let ((case-fold-search nil) + (start (point)) + macro-start + (delims (if comma-delim '(?\; ?,) '(?\;))) + (c-stmt-delim-chars (if comma-delim + c-stmt-delim-chars-with-comma + c-stmt-delim-chars)) + pos ; Current position. + boundary-pos ; Position of last boundary. + after-labels-pos ; Value of tok after first found colon. + last-label-pos ; Value of tok after last found colon. + sym ; Current symbol in the alphabet. + state ; Current state in the automaton. + saved-pos ; Current saved positions. + stack ; Stack of conses (state . saved-pos). + (cond-key (or c-opt-block-stmt-key + "\\<\\>")) ; Matches nothing. + (ret 'same) + tok ptok pptok ; Pos of last three sexps or bounds. + c-in-literal-cache c-maybe-labelp saved) + + (save-restriction + (if lim (narrow-to-region lim (point-max))) + + (if (save-excursion + (and (c-beginning-of-macro) + (/= (point) start))) + (setq macro-start (point))) + + ;; Try to skip over unary operator characters, to register + ;; that we've moved. + (while (progn + (setq pos (point)) + (c-backward-syntactic-ws) + (/= (skip-chars-backward "-+!*&~@`#") 0))) + + ;; First check for bare semicolon. Later on we ignore the + ;; boundaries for statements that doesn't contain any sexp. + ;; The only thing that is affected is that the error checking + ;; is a little less strict, and we really don't bother. + (if (and (memq (char-before) delims) + (progn (forward-char -1) + (setq saved (point)) + (c-backward-syntactic-ws) + (or (memq (char-before) delims) + (memq (char-before) '(?: nil)) + (eq (char-syntax (char-before)) ?\()))) + (setq ret 'previous + pos saved) + + ;; Begin at start and not pos to detect macros if we stand + ;; directly after the #. + (goto-char start) + (if (looking-at "\\<\\|\\W") + ;; Record this as the first token if not starting inside it. + (setq tok start)) + + (while + (catch 'loop ;; Throw nil to break, non-nil to continue. + (cond + ;; Check for macro start. + ((save-excursion + (and macro-start + (looking-at "[ \t]*[a-zA-Z0-9!]") + (progn (skip-chars-backward " \t") + (eq (char-before) ?#)) + (progn (setq saved (1- (point))) + (beginning-of-line) + (not (eq (char-before (1- (point))) ?\\))) + (progn (skip-chars-forward " \t") + (eq (point) saved)))) + (goto-char saved) + (if (and (c-forward-to-cpp-define-body) + (progn (c-forward-syntactic-ws start) + (< (point) start))) + ;; Stop at the first token in the content of the macro. + (setq pos (point) + ignore-labels t) ; Avoid the label check on exit. + (setq pos saved + ret 'macro + ignore-labels t)) + (throw 'loop nil)) + + ;; Do a round through the automaton if we found a + ;; boundary or if looking at a statement keyword. + ((or sym + (and (looking-at cond-key) + (setq sym (intern (match-string 1))))) + + (when (and (< pos start) (null stack)) + (throw 'loop nil)) - (setq c-maybe-labelp nil) - ;; see if we're in a literal. if not, then this bufpos may be - ;; a candidate for stopping - (cond - ;; CASE 0: did we hit the error condition above? - (donep) - ;; CASE 1: are we in a literal? - ((eq (c-in-literal lim) 'pound) - (beginning-of-line)) - ;; CASE 2: some other kind of literal? - ((c-in-literal lim)) - ;; CASE 3: are we looking at a conditional keyword? - ((or (and c-conditional-key (looking-at c-conditional-key)) - (and (eq (char-after) ?\() - (save-excursion - (c-forward-sexp 1) - (c-forward-syntactic-ws) - (not (eq (char-after) ?\;))) - (let ((here (point)) - (foundp (progn - (c-backward-syntactic-ws lim) - (forward-word -1) - (and lim - (<= lim (point)) - (not (c-in-literal lim)) - (not (eq (char-before) ?_)) - c-conditional-key - (looking-at c-conditional-key) - )))) - ;; did we find a conditional? - (if (not foundp) - (goto-char here)) - foundp))) - ;; are we in the middle of an else-if clause? - (if (save-excursion - (and (not substmt-p) - (c-safe (progn (c-forward-sexp -1) t)) - (looking-at "\\<else\\>[ \t\n]+\\<if\\>") - (not (c-in-literal lim)))) - (progn - (c-forward-sexp -1) - (c-backward-to-start-of-if lim))) - ;; are we sitting at an else clause, that we are not a - ;; substatement of? - (if (and (not substmt-p) - (looking-at "\\<else\\>[^_]")) - (c-backward-to-start-of-if lim)) - ;; a finally or a series of catches? - (if (not substmt-p) - (while (looking-at "\\<\\(catch\\|finally\\)\\>[^_]") - (c-safe (c-backward-sexp 2)) - (if (eq (char-after) ?\() - (c-safe (c-backward-sexp))))) - ;; are we sitting at the while of a do-while? - (if (and (looking-at "\\<while\\>[^_]") - (c-backward-to-start-of-do lim)) - (setq substmt-p nil)) - (setq last-begin (point) - donep substmt-p)) - ;; CASE 4: are we looking at a label? (But we handle - ;; switch labels later.) - ((and (looking-at c-label-key) - (not (looking-at "default\\>")) - (not (and (c-major-mode-is 'pike-mode) - (save-excursion - ;; Not inside a Pike type declaration? - (and (c-safe (backward-up-list 1) t) - (eq (char-after) ?\())))))) - ;; CASE 5: is this the first time we're checking? - (firstp (setq firstp nil - substmt-p (not (c-crosses-statement-barrier-p - (point) last-begin)) - last-begin (point))) - ;; CASE 6: have we crossed a statement barrier? - ((save-excursion - ;; Move over in-expression blocks before checking the - ;; barrier - (if (or (memq (char-after) '(?\( ?\[)) - (and (eq (char-after) ?{) - (c-looking-at-inexpr-block lim))) - (c-forward-sexp 1)) - (c-crosses-statement-barrier-p (point) last-begin)) - (setq donep t)) - ;; CASE 7: ignore labels - ((and c-maybe-labelp - (or (and c-access-key (looking-at c-access-key)) - ;; with switch labels, we have to go back further - ;; to try to pick up the case or default - ;; keyword. Potential bogosity alert: we assume - ;; `case' or `default' is first thing on line - (let ((here (point))) - (beginning-of-line) - (c-forward-syntactic-ws here) - (if (looking-at c-switch-label-key) - t - (goto-char here) - nil))))) - ;; CASE 8: ObjC or Java method def - ((and c-method-key - (setq last-begin (c-in-method-def-p))) - (setq donep t)) - ;; CASE 9: Normal token. At bob, we can end up at ws or a - ;; comment, and last-begin shouldn't be updated then. - ((not (looking-at "\\s \\|/[/*]")) - (setq last-begin (point))) - )))) - (goto-char last-begin) - ;; We always want to skip over the non-whitespace modifier - ;; characters that can start a statement. - (let ((lim (point))) - (skip-chars-backward "-+!*&~@`# \t\n" (c-point 'boi)) - (skip-chars-forward " \t\n" lim)))) + ;; The state handling. Continue in the common state for + ;; unhandled cases. + (or (cond + ((eq state 'else) + (if (eq sym 'boundary) + (setq state 'else-boundary) + (c-bos-report-error) + (c-bos-pop-state-and-retry))) + + ((eq state 'else-boundary) + (cond ((eq sym 'if) + (c-bos-pop-state (setq ret 'beginning))) + ((eq sym 'boundary) + (c-bos-report-error) + (c-bos-pop-state)))) + + ((eq state 'while) + (if (and (eq sym 'boundary) + ;; Since this can cause backtracking we do a + ;; little more careful analysis to avoid it: + ;; If there's a label in front of the while + ;; it can't be part of a do-while. + (not after-labels-pos)) + (progn (c-bos-save-pos) + (setq state 'while-boundary)) + (c-bos-pop-state-and-retry))) + + ((eq state 'while-boundary) + (cond ((eq sym 'do) + (c-bos-pop-state (setq ret 'beginning))) + ((eq sym 'boundary) + (c-bos-restore-pos) + (c-bos-pop-state)))) + + ((eq state 'catch) + (if (eq sym 'boundary) + (setq state 'catch-boundary) + (c-bos-report-error) + (c-bos-pop-state-and-retry))) + + ((eq state 'catch-boundary) + (cond + ((eq sym 'try) + (c-bos-pop-state (setq ret 'beginning))) + ((eq sym 'catch) + (setq state 'catch)) + ((eq sym 'boundary) + (c-bos-report-error) + (c-bos-pop-state))))) + + ;; This is state common. + (cond ((eq sym 'boundary) + (if (< pos start) + (c-bos-pop-state) + (c-bos-push-state))) + ((eq sym 'else) + (c-bos-push-state) + (c-bos-save-error-info 'if 'else) + (setq state 'else)) + ((eq sym 'while) + (when (or (not pptok) + (memq (char-after pptok) delims)) + ;; Since this can cause backtracking we do a + ;; little more careful analysis to avoid it: If + ;; the while isn't followed by a semicolon it + ;; can't be a do-while. + (c-bos-push-state) + (setq state 'while))) + ((memq sym '(catch finally)) + (c-bos-push-state) + (c-bos-save-error-info 'try sym) + (setq state 'catch)))) + + (when c-maybe-labelp + ;; We're either past a statement boundary or at the + ;; start of a statement, so throw away any label data + ;; for the previous one. + (setq after-labels-pos nil + last-label-pos nil + c-maybe-labelp nil)))) -(defun c-end-of-statement-1 () - (condition-case nil - (let (beg end found) - (while (and (not (eobp)) + ;; Step to next sexp, but not if we crossed a boundary, since + ;; that doesn't consume an sexp. + (if (eq sym 'boundary) + (setq ret 'previous) + (while (progn - (setq beg (point)) - (c-forward-sexp 1) - (setq end (point)) - (goto-char beg) - (setq found nil) - (while (and (not found) - (re-search-forward "[;{}]" end t)) - (if (not (c-in-literal beg)) - (setq found t))) - (not found))) - (goto-char end)) - (re-search-backward "[;{}]") - (forward-char 1)) - (error - (let ((beg (point))) - (c-safe (backward-up-list -1)) - (let ((end (point))) - (goto-char beg) - (search-forward ";" end 'move))) - ))) + (or (c-safe (goto-char (scan-sexps (point) -1)) t) + (throw 'loop nil)) + (cond ((looking-at "\\\\$") + ;; Step again if we hit a line continuation. + t) + (macro-start + ;; If we started inside a macro then this + ;; sexp is always interesting. + nil) + (t + ;; Otherwise check that we didn't step + ;; into a macro from the end. + (let ((macro-start + (save-excursion + (and (c-beginning-of-macro) + (point))))) + (when macro-start + (goto-char macro-start) + t)))))) + + ;; Check for statement boundary. + (when (save-excursion + (if (if (eq (char-after) ?{) + (c-looking-at-inexpr-block lim nil) + (eq (char-syntax (char-after)) ?\()) + ;; Need to move over parens and + ;; in-expression blocks to get a good start + ;; position for the boundary check. + (c-forward-sexp 1)) + (setq boundary-pos (c-crosses-statement-barrier-p + (point) pos))) + (setq pptok ptok + ptok tok + tok boundary-pos + sym 'boundary) + (throw 'loop t))) + + (when (and (numberp c-maybe-labelp) (not ignore-labels)) + ;; c-crosses-statement-barrier-p has found a colon, so + ;; we might be in a label now. + (if (not after-labels-pos) + (setq after-labels-pos tok)) + (setq last-label-pos tok + c-maybe-labelp t)) + + ;; ObjC method def? + (when (and c-opt-method-key + (setq saved (c-in-method-def-p))) + (setq pos saved + ignore-labels t) ; Avoid the label check on exit. + (throw 'loop nil)) + + (setq sym nil + pptok ptok + ptok tok + tok (point) + pos tok))) ; Not nil. + + ;; If the stack isn't empty there might be errors to report. + (while stack + (if (and (vectorp saved-pos) (eq (length saved-pos) 3)) + (c-bos-report-error)) + (setq saved-pos (cdr (car stack)) + stack (cdr stack))) + + (when (and (eq ret 'same) + (not (memq sym '(boundary ignore nil)))) + ;; Need to investigate closer whether we've crossed + ;; between a substatement and its containing statement. + (if (setq saved (if (looking-at c-block-stmt-1-key) + ptok + pptok)) + (cond ((> start saved) (setq pos saved)) + ((= start saved) (setq ret 'up))))) + + (when (and c-maybe-labelp (not ignore-labels) after-labels-pos) + ;; We're in a label. Maybe we should step to the statement + ;; after it. + (if (< after-labels-pos start) + (setq pos after-labels-pos) + (setq ret 'label) + (if (< last-label-pos start) + (setq pos last-label-pos))))) + + ;; Skip over the unary operators that can start the statement. + (goto-char pos) + (while (progn + (c-backward-syntactic-ws) + (/= (skip-chars-backward "-+!*&~@`#") 0)) + (setq pos (point))) + (goto-char pos) + ret))) + +(defun c-crosses-statement-barrier-p (from to) + "Return non-nil if buffer positions FROM to TO cross one or more +statement or declaration boundaries. The returned value is actually +the position of the earliest boundary char. + +The variable `c-maybe-labelp' is set to the position of the first `:' that +might start a label (i.e. not part of `::' and not preceded by `?'). If a +single `?' is found, then `c-maybe-labelp' is cleared." + (let ((skip-chars c-stmt-delim-chars) + lit-range) + (save-excursion + (catch 'done + (goto-char from) + (while (progn (skip-chars-forward skip-chars to) + (< (point) to)) + (if (setq lit-range (c-literal-limits from)) + (goto-char (setq from (cdr lit-range))) + (cond ((eq (char-after) ?:) + (forward-char) + (if (and (eq (char-after) ?:) + (< (point) to)) + ;; Ignore scope operators. + (forward-char) + (setq c-maybe-labelp (1- (point))))) + ((eq (char-after) ??) + ;; A question mark. Can't be a label, so stop + ;; looking for more : and ?. + (setq c-maybe-labelp nil + skip-chars (substring c-stmt-delim-chars 0 -2))) + (t (throw 'done (point)))))) + nil)))) -(defun c-crosses-statement-barrier-p (from to) - ;; Does buffer positions FROM to TO cross a C statement boundary? - (let ((here (point)) - (lim from) - crossedp) - (condition-case () - (progn - (goto-char from) - (while (and (not crossedp) - (< (point) to)) - (skip-chars-forward "^;{}:" (1- to)) - (if (not (c-in-literal lim)) - (progn - (if (memq (char-after) '(?\; ?{ ?})) - (setq crossedp t) - (if (eq (char-after) ?:) - (setq c-maybe-labelp t)) - (forward-char 1)) - (setq lim (point))) - (forward-char 1)))) - (error (setq crossedp nil))) - (goto-char here) - crossedp)) +;; This is a dynamically bound cache used together with +;; c-query-macro-start and c-query-and-set-macro-start. It only works +;; as long as point doesn't cross a macro boundary. +(defvar c-macro-start 'unknown) + +(defsubst c-query-and-set-macro-start () + (if (symbolp c-macro-start) + (setq c-macro-start (save-excursion + (and (c-beginning-of-macro) + (point)))) + c-macro-start)) + +(defsubst c-query-macro-start () + (if (symbolp c-macro-start) + (save-excursion + (and (c-beginning-of-macro) + (point))) + c-macro-start)) + +(defun c-beginning-of-macro (&optional lim) + "Go to the beginning of a cpp macro definition. +Leave point at the beginning of the macro and return t if in a cpp +macro definition, otherwise return nil and leave point unchanged." + (let ((here (point))) + (save-restriction + (if lim (narrow-to-region lim (point-max))) + (beginning-of-line) + (while (eq (char-before (1- (point))) ?\\) + (forward-line -1)) + (back-to-indentation) + (if (and (<= (point) here) + (looking-at "#[ \t]*[a-zA-Z0-9!]")) + t + (goto-char here) + nil)))) + +(defun c-end-of-macro () + "Go to the end of a cpp macro definition. +More accurately, move point to the end of the closest following line +that doesn't end with a line continuation backslash." + (while (progn + (end-of-line) + (when (and (eq (char-before) ?\\) + (not (eobp))) + (forward-char) + t)))) - -(defun c-beginning-of-macro (&optional lim) - ;; Go to the beginning of a cpp macro definition. Leaves point at - ;; the beginning of the macro and returns t if in a cpp macro - ;; definition, otherwise returns nil and leaves point unchanged. - ;; `lim' is currently ignored, but the interface requires it. +(defun c-forward-comment (count) + ;; Insulation from various idiosyncrasies in implementations of + ;; `forward-comment'. + ;; + ;; Note: Some emacsen considers incorrectly that any line comment + ;; ending with a backslash continues to the next line. I can't + ;; think of any way to work around that in a reliable way without + ;; changing the buffer, though. Suggestions welcome. ;) (No, + ;; temporarily changing the syntax for backslash doesn't work since + ;; we must treat escapes in string literals correctly.) + ;; + ;; Another note: When moving backwards over a block comment, there's + ;; a bug in forward-comment that can make it stop at "/*" inside a + ;; line comment. Haven't yet found a reasonably cheap way to kludge + ;; around that one either. :\ (let ((here (point))) - (beginning-of-line) - (while (eq (char-before (1- (point))) ?\\) - (forward-line -1)) - (back-to-indentation) - (if (and (<= (point) here) - (eq (char-after) ?#)) - t - (goto-char here) - nil))) + (if (>= count 0) + (when (forward-comment count) + (if (eobp) + ;; Some emacsen (e.g. XEmacs 21) return t when moving + ;; forwards at eob. + nil + ;; Emacs includes the ending newline in a b-style (c++) + ;; comment, but XEmacs doesn't. We depend on the Emacs + ;; behavior (which also is symmetric). + (if (and (eolp) (nth 7 (parse-partial-sexp here (point)))) + (condition-case nil (forward-char 1))) + t)) + ;; When we got newline terminated comments, + ;; forward-comment in all supported emacsen so far will + ;; stop at eol of each line not ending with a comment when + ;; moving backwards. The following corrects for it when + ;; count is -1. The other common case, when count is + ;; large and negative, works regardless. It's too much + ;; work to correct for the rest of the cases. + (skip-chars-backward " \t\n\r\f") + (if (bobp) + ;; Some emacsen return t when moving backwards at bob. + nil + (re-search-forward "[\n\r]" here t) + (let* ((res (if (forward-comment count) + (if (eolp) (forward-comment -1) t))) + (savepos (point))) + ;; XEmacs treats line continuations as whitespace (but only + ;; in the backward direction). + (while (and (progn (end-of-line) (< (point) here)) + (eq (char-before) ?\\)) + (setq res nil + savepos (point)) + (forward-line)) + (goto-char savepos) + res))))) -;; Skipping of "syntactic whitespace", defined as lexical whitespace, -;; C and C++ style comments, and preprocessor directives. Search no -;; farther back or forward than optional LIM. If LIM is omitted, -;; `beginning-of-defun' is used for backward skipping, point-max is -;; used for forward skipping. +(defun c-forward-comment-lc (count) + ;; Like `c-forward-comment', but treat line continuations as + ;; whitespace. + (catch 'done + (if (> count 0) + (while (if (c-forward-comment 1) + (progn + (setq count (1- count)) + (> count 0)) + (if (looking-at "\\\\$") + (progn + (forward-char) + t) + (throw 'done nil)))) + (while (if (c-forward-comment -1) + (progn + (setq count (1+ count)) + (< count 0)) + (if (and (eolp) (eq (char-before) ?\\)) + (progn + (backward-char) + t) + (throw 'done nil))))) + t)) (defun c-forward-syntactic-ws (&optional lim) - ;; Forward skip of syntactic whitespace for Emacs 19. - (let* ((here (point-max)) - (hugenum (point-max))) + "Forward skip of syntactic whitespace. +Syntactic whitespace is defined as whitespace characters, comments, +and preprocessor directives. However if point starts inside a comment +or preprocessor directive, the content of it is not treated as +whitespace. LIM sets an upper limit of the forward movement, if +specified." + (let ((here (point-max))) + (or lim (setq lim here)) (while (/= here (point)) + ;; If forward-comment in at least XEmacs 21 is given a large + ;; positive value, it'll loop all the way through if it hits eob. + (while (c-forward-comment 5)) (setq here (point)) - (c-forward-comment hugenum) - ;; skip preprocessor directives - (when (and (eq (char-after) ?#) - (= (c-point 'boi) (point))) - (while (and (eq (char-before (c-point 'eol)) ?\\) - (= (forward-line 1) 0))) - (end-of-line)) - ) - (if lim (goto-char (min (point) lim))))) + (cond + ;; Skip line continuations. + ((looking-at "\\\\$") + (forward-char)) + ;; Skip preprocessor directives. + ((and (looking-at "#[ \t]*[a-zA-Z0-9!]") + (progn (skip-chars-backward " \t") + (bolp))) + (end-of-line) + (while (and (<= (point) lim) + (eq (char-before) ?\\) + (= (forward-line 1) 0)) + (end-of-line)) + (when (> (point) lim) + ;; Don't move past the macro if that'd take us past the limit. + (goto-char here))) + ;; Skip in-comment line continuations (used for Pike refdoc). + ((and c-opt-in-comment-lc (looking-at c-opt-in-comment-lc)) + (goto-char (match-end 0))))) + (goto-char (min (point) lim)))) (defun c-backward-syntactic-ws (&optional lim) - ;; Backward skip over syntactic whitespace for Emacs 19. - (let* ((here (point-min)) - (hugenum (- (point-max)))) + "Backward skip of syntactic whitespace. +Syntactic whitespace is defined as whitespace characters, comments, +and preprocessor directives. However if point starts inside a comment +or preprocessor directive, the content of it is not treated as +whitespace. LIM sets a lower limit of the backward movement, if +specified." + (let ((start-line (c-point 'bol)) + (here (point-min)) + (line-cont 'maybe) + prev-pos) + (or lim (setq lim here)) (while (/= here (point)) + (setq prev-pos (point)) + ;; If forward-comment in Emacs 19.34 is given a large negative + ;; value, it'll loop all the way through if it hits bob. + (while (c-forward-comment -5)) (setq here (point)) - (c-forward-comment hugenum) - (c-beginning-of-macro)) - (if lim (goto-char (max (point) lim))))) - - -;; Moving by tokens, where a token is defined as all symbols and -;; identifiers which aren't syntactic whitespace (note that "->" is -;; considered to be two tokens). Point is always either left at the -;; beginning of a token or not moved at all. COUNT specifies the -;; number of tokens to move; a negative COUNT moves in the opposite -;; direction. A COUNT of 0 moves to the next token beginning only if -;; not already at one. If BALANCED is true, move over balanced -;; parens, otherwise move into them. Also, if BALANCED is true, never -;; move out of an enclosing paren. LIM sets the limit for the -;; movement and defaults to the point limit. Returns the number of -;; tokens left to move (positive or negative). If BALANCED is true, a -;; move over a balanced paren counts as one. Note that if COUNT is 0 -;; and no appropriate token beginning is found, 1 will be returned. -;; Thus, a return value of 0 guarantees that point is at the requested -;; position and a return value less (without signs) than COUNT -;; guarantees that point is at the beginning of some token. + (cond + ((and (eolp) + (eq (char-before) ?\\) + (if (<= prev-pos (c-point 'eonl)) + t + ;; Passed a line continuation, but not from the line we + ;; started on. + (forward-char) + (setq line-cont nil))) + (backward-char) + (setq line-cont t)) + ((progn + (when (eq line-cont 'maybe) + (save-excursion + (end-of-line) + (setq line-cont (eq (char-before) ?\\)))) + (or line-cont + (and (< (point) start-line) + (c-beginning-of-macro)))) + (if (< (point) lim) + ;; Don't move past the macro if we began inside it or at + ;; the end of the same line, or if the move would take us + ;; past the limit. + (goto-char here)) + (setq line-cont nil)) + ;; Skip in-comment line continuations (used for Pike refdoc). + ((and c-opt-in-comment-lc + (save-excursion + (and (c-safe (beginning-of-line) + (backward-char 2) + t) + (looking-at c-opt-in-comment-lc) + (eq (match-end 0) here)))) + (goto-char (match-beginning 0))))) + (goto-char (max (point) lim)))) (defun c-forward-token-1 (&optional count balanced lim) + "Move forward by tokens. +A token is defined as all symbols and identifiers which aren't +syntactic whitespace \(note that e.g. \"->\" is considered to be two +tokens). Point is always either left at the beginning of a token or +not moved at all. COUNT specifies the number of tokens to move; a +negative COUNT moves in the opposite direction. A COUNT of 0 moves to +the next token beginning only if not already at one. If BALANCED is +true, move over balanced parens, otherwise move into them. Also, if +BALANCED is true, never move out of an enclosing paren. LIM sets the +limit for the movement and defaults to the point limit. + +Return the number of tokens left to move \(positive or negative). If +BALANCED is true, a move over a balanced paren counts as one. Note +that if COUNT is 0 and no appropriate token beginning is found, 1 will +be returned. Thus, a return value of 0 guarantees that point is at +the requested position and a return value less \(without signs) than +COUNT guarantees that point is at the beginning of some token." (or count (setq count 1)) (if (< count 0) (- (c-backward-token-1 (- count) balanced lim)) @@ -371,7 +763,7 @@ (if (memq (char-syntax (char-after)) jump-syntax) (goto-char (scan-sexps (point) 1)) (forward-char)) - (c-forward-syntactic-ws lim) + (c-forward-syntactic-ws) (setq count (1- count))) (error (goto-char last))) (when (eobp) @@ -380,6 +772,8 @@ count))) (defun c-backward-token-1 (&optional count balanced lim) + "Move backward by tokens. +See `c-forward-token-1' for details." (or count (setq count 1)) (if (< count 0) (- (c-forward-token-1 (- count) balanced lim)) @@ -391,7 +785,9 @@ (or (and (memq (char-syntax (or (char-after) ? )) '(?w ?_)) (memq (char-syntax (or (char-before) ? )) '(?w ?_))) (/= (point) - (save-excursion (c-forward-syntactic-ws) (point))) + (save-excursion + (c-forward-syntactic-ws (1+ lim)) + (point))) (eobp))) ;; If count is zero we should jump if in the middle of a ;; token or if there is whitespace between point and the @@ -406,7 +802,7 @@ (while (progn (setq last (point)) (> count 0)) - (c-backward-syntactic-ws lim) + (c-backward-syntactic-ws) (if (memq (char-syntax (char-before)) jump-syntax) (goto-char (scan-sexps (point) -1)) (backward-char)) @@ -415,16 +811,93 @@ (if (bobp) (goto-char last))))) count))) +(defun c-syntactic-re-search-forward (regexp &optional bound noerror count + paren-level) + ;; Like `re-search-forward', but only report matches that are found + ;; in syntactically significant text. I.e. matches that begins in + ;; comments, macros or string literals are ignored. The start point + ;; is assumed to be outside any comment, macro or string literal, or + ;; else the content of that region is taken as syntactically + ;; significant text. If PAREN-LEVEL is non-nil, an additional + ;; restriction is added to ignore matches in nested paren sexps, and + ;; the search will also not go outside the current paren sexp. + (or bound (setq bound (point-max))) + (or count (setq count 1)) + (if paren-level (setq paren-level -1)) + (let ((start (point)) + (pos (point)) + match-pos state) + (condition-case err + (while (and (> count 0) + (re-search-forward regexp bound noerror)) + (setq match-pos (point) + state (parse-partial-sexp pos (match-beginning 0) + paren-level nil state) + pos (point)) + (cond ((nth 3 state) + ;; Match inside a string. Skip to the end of it + ;; before continuing. + (let ((ender (make-string 1 (nth 3 state)))) + (while (progn + (search-forward ender bound noerror) + (setq state (parse-partial-sexp pos (point) + nil nil state) + pos (point)) + (nth 3 state))))) + ((nth 7 state) + ;; Match inside a line comment. Skip to eol. Use + ;; re-search-forward for it to get the right bound + ;; behavior. + (re-search-forward "[\n\r]" bound noerror)) + ((nth 4 state) + ;; Match inside a block comment. Skip to the '*/'. + (re-search-forward "\\*/" bound noerror)) + ((save-excursion (c-beginning-of-macro start)) + ;; Match inside a macro. Skip to the end of it. + (c-end-of-macro)) + ((and paren-level (/= (car state) 0)) + (if (> (car state) 0) + ;; Match inside a nested paren sexp. Skip out of it. + (setq state (parse-partial-sexp pos bound 0 nil state) + pos (point)) + ;; Have exited the current paren sexp. The + ;; parse-partial-sexp above has left us just after + ;; the closing paren in this case. Just make + ;; re-search-forward above fail in the appropriate + ;; way; we'll adjust the leave off point below if + ;; necessary. + (setq bound (point)))) + (t + ;; A real match. + (setq count (1- count))))) + (error + (goto-char start) + (signal (car err) (cdr err)))) + (if (= count 0) + (progn + (goto-char match-pos) + match-pos) + ;; Search failed. Set point as appropriate. + (cond ((eq noerror t) + (goto-char start)) + (paren-level + (if (eq (car (parse-partial-sexp pos bound -1 nil state)) -1) + (backward-char))) + (t + (goto-char bound))) + nil))) + -;; Return `c' if in a C-style comment, `c++' if in a C++ style -;; comment, `string' if in a string literal, `pound' if on a -;; preprocessor line, or nil if not in a comment at all. Optional LIM -;; is used as the backward limit of the search. If omitted, or nil, -;; `beginning-of-defun' is used." +(defun c-in-literal (&optional lim detect-cpp) + "Return the type of literal point is in, if any. +The return value is `c' if in a C-style comment, `c++' if in a C++ +style comment, `string' if in a string literal, `pound' if DETECT-CPP +is non-nil and on a preprocessor line, or nil if somewhere else. +Optional LIM is used as the backward limit of the search. If omitted, +or nil, `c-beginning-of-defun' is used. -(defun c-in-literal (&optional lim) - ;; Determine if point is in a C++ literal. we cache the last point - ;; calculated if the cache is enabled +The last point calculated is cached if the cache is enabled, i.e. if +`c-in-literal-cache' is bound to a two element vector." (if (and (vectorp c-in-literal-cache) (= (point) (aref c-in-literal-cache 0))) (aref c-in-literal-cache 1) @@ -434,7 +907,7 @@ (cond ((nth 3 state) 'string) ((nth 4 state) (if (nth 7 state) 'c++ 'c)) - ((c-beginning-of-macro lim) 'pound) + ((and detect-cpp (c-beginning-of-macro lim)) 'pound) (t nil)))))) ;; cache this result if the cache is enabled (if (not c-in-literal-cache) @@ -443,30 +916,30 @@ ;; XEmacs has a built-in function that should make this much quicker. ;; I don't think we even need the cache, which makes our lives more -;; complicated anyway. In this case, lim is ignored. -(defun c-fast-in-literal (&optional lim) +;; complicated anyway. In this case, lim is only used to detect +;; cpp directives. +(defun c-fast-in-literal (&optional lim detect-cpp) (let ((context (buffer-syntactic-context))) (cond ((eq context 'string) 'string) ((eq context 'comment) 'c++) ((eq context 'block-comment) 'c) - ((save-excursion (c-beginning-of-macro lim)) 'pound)))) + ((and detect-cpp (save-excursion (c-beginning-of-macro lim))) 'pound)))) (if (fboundp 'buffer-syntactic-context) (defalias 'c-in-literal 'c-fast-in-literal)) (defun c-literal-limits (&optional lim near not-in-delimiter) - ;; Returns a cons of the beginning and end positions of the comment - ;; or string surrounding point (including both delimiters), or nil - ;; if point isn't in one. If LIM is non-nil, it's used as the - ;; "safe" position to start parsing from. If NEAR is non-nil, then - ;; the limits of any literal next to point is returned. "Next to" - ;; means there's only [ \t] between point and the literal. The - ;; search for such a literal is done first in forward direction. If - ;; NOT-IN-DELIMITER is non-nil, the case when point is inside a - ;; starting delimiter won't be recognized. This only has effect for - ;; comments, which have starting delimiters with more than one - ;; character. + "Return a cons of the beginning and end positions of the comment or +string surrounding point (including both delimiters), or nil if point +isn't in one. If LIM is non-nil, it's used as the \"safe\" position +to start parsing from. If NEAR is non-nil, then the limits of any +literal next to point is returned. \"Next to\" means there's only [ +\t] between point and the literal. The search for such a literal is +done first in forward direction. If NOT-IN-DELIMITER is non-nil, the +case when point is inside a starting delimiter won't be recognized. +This only has effect for comments, which have starting delimiters with +more than one character." (save-excursion (let* ((pos (point)) (lim (or lim (c-point 'bod))) @@ -586,12 +1059,12 @@ (defalias 'c-literal-limits 'c-literal-limits-fast)) (defun c-collect-line-comments (range) - ;; If the argument is a cons of two buffer positions (such as - ;; returned by c-literal-limits), and that range contains a C++ - ;; style line comment, then an extended range is returned that - ;; contains all adjacent line comments (i.e. all comments that - ;; starts in the same column with no empty lines or non-whitespace - ;; characters between them). Otherwise the argument is returned. + "If the argument is a cons of two buffer positions (such as returned by +`c-literal-limits'), and that range contains a C++ style line comment, +then an extended range is returned that contains all adjacent line +comments (i.e. all comments that starts in the same column with no +empty lines or non-whitespace characters between them). Otherwise the +argument is returned." (save-excursion (condition-case nil (if (and (consp range) (progn @@ -620,10 +1093,10 @@ (error range)))) (defun c-literal-type (range) - ;; Convenience function that given the result of c-literal-limits, - ;; returns nil or the type of literal that the range surrounds. - ;; It's much faster than using c-in-literal and is intended to be - ;; used when you need both the type of a literal and its limits. + "Convenience function that given the result of `c-literal-limits', +returns nil or the type of literal that the range surrounds. It's +much faster than using `c-in-literal' and is intended to be used when +you need both the type of a literal and its limits." (if (consp range) (save-excursion (goto-char (car range)) @@ -635,215 +1108,301 @@ ;; utilities for moving and querying around syntactic elements -(defvar c-parsing-error nil) + +(defvar c-state-cache nil) +(make-variable-buffer-local 'c-state-cache) +;; The state cache used by `c-parse-state' to cut down the amount of +;; searching. It's the result from some earlier `c-parse-state' call. +;; The use of the cached info is more effective if the next +;; `c-parse-state' call is on a line close by the one the cached state +;; was made at; the cache can actually slow down a little if the +;; cached state was made very far back in the buffer. The cache is +;; most effective if `c-parse-state' is used on each line while moving +;; forward. + +(defvar c-state-cache-start nil) +;; This (point-min) when `c-state-cache' was calculated, to detect +;; that the start point hasn't changed due to narrowing. (defun c-parse-state () - ;; Finds and records all open parens between some important point - ;; earlier in the file and point. + ;; Finds and records 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. ;; - ;; if there's a state cache, return it - (if c-state-cache c-state-cache - (let* (at-bob - (pos (save-excursion + ;; 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 + ;; 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 is recorded, + ;; and thus the state never contains two cons elements in + ;; succession. + (save-restriction + (let* ((here (point)) + (c-macro-start (c-query-macro-start)) + (in-macro-start (or c-macro-start (point))) + old-state last-pos pairs pos) + ;; Somewhat ugly use of c-check-state-cache to get rid of the + ;; part of the state cache that is after point. Can't use + ;; c-whack-state-after for the same reasons as in that function. + (c-check-state-cache (point) nil nil) + ;; 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))))) + ;; Check if the found 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. + (when last-pos + (save-excursion + (goto-char last-pos) + (when (and (c-beginning-of-macro) + (/= (point) in-macro-start)) + (c-check-state-cache (point) nil nil) + ;; Set last-pos again, just like above. + (setq last-pos (and c-state-cache + (if (consp (car c-state-cache)) + (cdr (car c-state-cache)) + (1+ (car c-state-cache)))))))) + (setq pos + ;; Find the start position for the forward search. (Can't + ;; search in the backward direction since point might be + ;; in some kind of literal.) + (or (when last-pos + ;; 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))))) + (when c-state-cache + (setq old-state c-state-cache) + last-pos)) + (save-excursion ;; go back 2 bods, but ignore any bogus positions - ;; returned by beginning-of-defun (i.e. open paren - ;; in column zero) + ;; returned by beginning-of-defun (i.e. open paren in + ;; column zero) + (goto-char here) (let ((cnt 2)) - (while (not (or at-bob (zerop cnt))) - (goto-char (c-point 'bod)) - (if (and - (eq (char-after) ?\{) - ;; The following catches an obscure special - ;; case where the brace is preceded by an - ;; open paren. That can only legally occur - ;; with blocks inside expressions and in - ;; Pike special brace lists. Even so, this - ;; test is still bogus then, but hopefully - ;; good enough. (We don't want to use - ;; up-list here since it might be slow.) - (save-excursion - (c-backward-syntactic-ws) - (not (eq (char-before) ?\()))) - (setq cnt (1- cnt))) - (if (bobp) - (setq at-bob t)))) - (point))) - (here (save-excursion - ;;(skip-chars-forward " \t}") - (point))) - (last-bod here) (last-pos pos) - placeholder state sexp-end) - ;; cache last bod position - (while (catch 'backup-bod - (setq state nil) - (while (and pos (< pos here)) - (setq last-pos pos) - (if (and (setq pos (c-safe (scan-lists pos 1 -1))) - (<= pos here)) - (progn - (setq sexp-end (c-safe (scan-sexps (1- pos) 1))) - (if (and sexp-end - (<= sexp-end here)) - ;; we want to record both the start and end - ;; of this sexp, but we only want to record - ;; the last-most of any of them before here - (progn - (if (eq (char-after (1- pos)) ?\{) - (setq state (cons (cons (1- pos) sexp-end) - (if (consp (car state)) - (cdr state) - state)))) - (setq pos sexp-end)) - ;; we're contained in this sexp so put pos on - ;; front of list - (setq state (cons (1- pos) state)))) - ;; something bad happened. check to see if we - ;; crossed an unbalanced close brace. if so, we - ;; didn't really find the right `important bufpos' - ;; so lets back up and try again - (if (and (not pos) (not at-bob) - (setq placeholder - (c-safe (scan-lists last-pos 1 1))) - ;;(char-after (1- placeholder)) - (<= placeholder here) - (eq (char-after (1- placeholder)) ?\})) - (while t - (setq last-bod (c-safe (scan-lists last-pos -1 1))) - (if (not last-bod) - (save-excursion - ;; bogus, but what can we do here? - (goto-char placeholder) - (beginning-of-line) - (setq c-parsing-error - (format "\ -Unbalanced close brace at line %d" (1+ (count-lines 1 (point))))) - (throw 'backup-bod nil)) - (setq at-bob (= last-bod (point-min)) - pos last-bod) - (if (= (char-after last-bod) ?\{) - (throw 'backup-bod t))) - )) ;end-if - )) ;end-while - nil)) - state))) + (while (not (or (bobp) (zerop cnt))) + (c-beginning-of-defun-1) + (if (eq (char-after) ?\{) + (setq cnt (1- cnt))))) + (point)))) + (narrow-to-region (point-min) here) + (while pos + ;; Find the balanced brace pairs. + (setq pairs nil) + (while (and (setq last-pos (c-down-list-forward pos)) + (setq pos (c-up-list-forward last-pos))) + (if (eq (char-before last-pos) ?{) + (setq pairs (cons (cons last-pos pos) pairs)))) + ;; Should ignore any pairs that are in a macro, providing + ;; we're not in the same one. + (when (and pairs (< (car (car pairs)) in-macro-start)) + (while (and (save-excursion + (goto-char (car (car pairs))) + (c-beginning-of-macro)) + (setq pairs (cdr pairs))))) + ;; Record the last brace pair. + (when pairs + (if (and (eq c-state-cache old-state) + (consp (car-safe c-state-cache))) + ;; There's a closed pair on the cached state but we've + ;; found a later one, so remove it. + (setq c-state-cache (cdr c-state-cache))) + (setq pairs (car pairs)) + (setcar pairs (1- (car pairs))) + (setq c-state-cache (cons pairs 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. + (progn + (setq pos last-pos) + (if (or (>= last-pos in-macro-start) + (save-excursion + (goto-char last-pos) + (not (c-beginning-of-macro)))) + (setq c-state-cache (cons (1- 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) + (unless 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)))) + c-state-cache))) -(defun c-whack-state (bufpos state) - ;; whack off any state information that appears on STATE which lies - ;; after the bounds of BUFPOS. - (let (newstate car) - (while state - (setq car (car state) - state (cdr state)) - (if (consp car) - ;; just check the car, because in a balanced brace - ;; expression, it must be impossible for the corresponding - ;; close brace to be before point, but the open brace to be - ;; after. - (if (<= bufpos (car car)) - nil ; whack it off - ;; its possible that the open brace is before bufpos, but - ;; the close brace is after. In that case, convert this - ;; to a non-cons element. - (if (<= bufpos (cdr car)) - (setq newstate (append newstate (list (car car)))) - ;; we know that both the open and close braces are - ;; before bufpos, so we also know that everything else - ;; on state is before bufpos, so we can glom up the - ;; whole thing and exit. - (setq newstate (append newstate (list car) state) - state nil))) - (if (<= bufpos car) - nil ; whack it off - ;; it's before bufpos, so everything else should too - (setq newstate (append newstate (list car) state) - state nil)))) - newstate)) +;; Debug tool to catch cache inconsistencies. +(defvar c-debug-parse-state nil) +(unless (fboundp 'c-real-parse-state) + (fset 'c-real-parse-state (symbol-function 'c-parse-state))) +(cc-bytecomp-defun c-real-parse-state) +(defun c-debug-parse-state () + (let ((res1 (c-real-parse-state)) res2) + (let ((c-state-cache nil)) + (setq res2 (c-real-parse-state))) + (unless (equal res1 res2) + (error "c-parse-state inconsistency: using cache: %s, from scratch: %s" + res1 res2)) + res1)) +(defun c-toggle-parse-state-debug (&optional arg) + (interactive "P") + (setq c-debug-parse-state (c-calculate-state arg c-debug-parse-state)) + (fset 'c-parse-state (symbol-function (if c-debug-parse-state + 'c-debug-parse-state + 'c-real-parse-state))) + (c-keep-region-active)) + +(defun c-check-state-cache (beg end old-length) + ;; Used on `after-change-functions' to adjust `c-state-cache'. + ;; Prefer speed to finesse here, since there will be many more calls + ;; to this function than times `c-state-cache' is used. + ;; + ;; 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. + (if (not (eq c-state-cache-start (point-min))) + (setq c-state-cache-start (point-min) + c-state-cache nil) + (while (and c-state-cache + (let ((elem (car c-state-cache))) + (if (consp elem) + (or (<= beg (car elem)) + (< beg (cdr elem))) + (<= beg elem)))) + (setq c-state-cache (cdr c-state-cache))))) -(defun c-hack-state (bufpos which state) - ;; Using BUFPOS buffer position, and WHICH (must be 'open or - ;; 'close), hack the c-parse-state STATE and return the results. - (if (eq which 'open) - (let ((car (car state))) - (if (or (null car) - (consp car) - (/= bufpos car)) - (cons bufpos state) - state)) - (if (not (eq which 'close)) - (error "c-hack-state, bad argument: %s" which)) - ;; 'close brace - (let ((car (car state)) - (cdr (cdr state))) - (if (consp car) - (setq car (car cdr) - cdr (cdr cdr))) - ;; TBD: is this test relevant??? - (if (consp car) - state ;on error, don't change - ;; watch out for balanced expr already on cdr of list - (cons (cons car bufpos) - (if (consp (car cdr)) - (cdr cdr) cdr)) - )))) +(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. + (let* ((newstate (list nil)) + (ptr newstate) + car) + (while paren-state + (setq car (car paren-state) + paren-state (cdr paren-state)) + (if (< (if (consp car) (car car) car) bufpos) + (setq paren-state nil) + (setcdr ptr (list car)) + (setq ptr (cdr ptr)))) + (cdr newstate))) -(defun c-adjust-state (from to shift state) - ;; Adjust all points in state that lie in the region FROM..TO by - ;; SHIFT amount. - (mapcar - (function - (lambda (e) - (if (consp e) - (let ((car (car e)) - (cdr (cdr e))) - (if (and (<= from car) (< car to)) - (setcar e (+ shift car))) - (if (and (<= from cdr) (< cdr to)) - (setcdr e (+ shift cdr)))) - (if (and (<= from e) (< e to)) - (setq e (+ shift e)))) - e)) - state)) +(defun c-whack-state-after (bufpos paren-state) + ;; Whack off any state information from PAREN-STATE which lies at or + ;; after BUFPOS. Not destructive on PAREN-STATE. + (catch 'done + (while paren-state + (let ((car (car paren-state))) + (if (consp car) + ;; just check the car, because in a balanced brace + ;; expression, it must be impossible for the corresponding + ;; close brace to be before point, but the open brace to + ;; be after. + (if (<= bufpos (car car)) + nil ; whack it off + (if (< bufpos (cdr car)) + ;; its possible that the open brace is before + ;; bufpos, but the close brace is after. In that + ;; case, convert this to a non-cons element. The + ;; rest of the state is before bufpos, so we're + ;; done. + (throw 'done (cons (car car) (cdr paren-state))) + ;; we know that both the open and close braces are + ;; before bufpos, so we also know that everything else + ;; on state is before bufpos. + (throw 'done paren-state))) + (if (<= bufpos car) + nil ; whack it off + ;; it's before bufpos, so everything else should too. + (throw 'done paren-state))) + (setq paren-state (cdr paren-state))) + nil))) (defun c-beginning-of-inheritance-list (&optional lim) ;; Go to the first non-whitespace after the colon that starts a ;; multiple inheritance introduction. Optional LIM is the farthest ;; back we should search. - (let* ((lim (or lim (c-point 'bod))) - (placeholder (progn - (back-to-indentation) - (point))) - (chr (char-after))) - (c-backward-syntactic-ws lim) - (while (and (> (point) lim) - (or (eq chr ?,) - (memq (char-before) '(?, ?:))) - (progn - (beginning-of-line) - (setq placeholder (point)) - (skip-chars-forward " \t") - (setq chr (char-after)) - (not (looking-at c-class-key)) - )) - (c-backward-syntactic-ws lim)) - (goto-char placeholder) - (skip-chars-forward "^:" (c-point 'eol)))) + (let* ((lim (or lim (c-point 'bod)))) + (c-with-syntax-table c++-template-syntax-table + (c-backward-token-1 0 t lim) + (while (and (looking-at "[_a-zA-Z<,]") + (= (c-backward-token-1 1 t lim) 0))) + (skip-chars-forward "^:")))) (defun c-in-method-def-p () ;; Return nil if we aren't in a method definition, otherwise the ;; position of the initial [+-]. (save-excursion (beginning-of-line) - (and c-method-key - (looking-at c-method-key) + (and c-opt-method-key + (looking-at c-opt-method-key) (point)) )) +;; Contributed by Kevin Ryde <user42@zip.com.au>. +(defun c-in-gcc-asm-p () + ;; Return non-nil if point is within a gcc \"asm\" block. + ;; + ;; This should be called with point inside an argument list. + ;; + ;; Only one level of enclosing parentheses is considered, so for + ;; instance `nil' is returned when in a function call within an asm + ;; operand. + + (and c-opt-asm-stmt-key + (save-excursion + (beginning-of-line) + (backward-up-list 1) + (c-beginning-of-statement-1 (point-min) nil t) + (looking-at c-opt-asm-stmt-key)))) + (defun c-at-toplevel-p () "Return a determination as to whether point is at the `top-level'. Being at the top-level means that point is either outside any -enclosing block (such function definition), or inside a class -definition, but outside any method blocks. +enclosing block (such function definition), or inside a class, +namespace or extern definition, but outside any method blocks. If point is not at the top-level (e.g. it is inside a method definition), then nil is returned. Otherwise, if point is at a @@ -852,25 +1411,42 @@ buffer position of the start of the class declaration, and the first element is the buffer position of the enclosing class's opening brace." - (let ((state (c-parse-state))) - (or (not (c-most-enclosing-brace state)) - (c-search-uplist-for-classkey state)))) + (let ((paren-state (c-parse-state))) + (or (not (c-most-enclosing-brace paren-state)) + (c-search-uplist-for-classkey paren-state)))) -(defun c-just-after-func-arglist-p (&optional containing) +(defun c-forward-to-cpp-define-body () + ;; Assuming point is at the "#" that introduces a preprocessor + ;; directive, it's moved forward to the start of the definition body + ;; if it's a "#define". Non-nil is returned in this case, in all + ;; other cases nil is returned and point isn't moved. + (when (and (looking-at + (concat "#[ \t]*" + "define[ \t]+\\(\\sw\\|_\\)+\\(\([^\)]*\)\\)?" + "\\([ \t]\\|\\\\\n\\)*")) + (not (= (match-end 0) (c-point 'eol)))) + (goto-char (match-end 0)))) + +(defun c-just-after-func-arglist-p (&optional containing lim) ;; Return t if we are between a function's argument list closing ;; paren and its opening brace. Note that the list close brace ;; could be followed by a "const" specifier or a member init hanging ;; colon. Optional CONTAINING is position of containing s-exp open - ;; brace. If not supplied, point is used as search start. + ;; brace. If not supplied, point is used as search start. LIM is + ;; used as bound for some backward buffer searches; the search might + ;; continue past it. + ;; + ;; Note: This test is easily fooled. It only works reasonably well + ;; in the situations where `c-guess-basic-syntax' uses it. (save-excursion - (c-backward-syntactic-ws) + (c-backward-syntactic-ws lim) (let ((checkpoint (or containing (point)))) (goto-char checkpoint) ;; could be looking at const specifier (if (and (eq (char-before) ?t) (forward-word -1) - (looking-at "\\<const\\>")) - (c-backward-syntactic-ws) + (looking-at "\\<const\\>[^_]")) + (c-backward-syntactic-ws lim) ;; otherwise, we could be looking at a hanging member init ;; colon (goto-char checkpoint) @@ -882,22 +1458,30 @@ (if (eq (char-before) ?\)) (c-backward-sexp 2) (c-backward-sexp 1)) - (c-backward-syntactic-ws)) + (c-backward-syntactic-ws lim)) (if (and (eq (char-before) ?:) (progn (forward-char -1) - (c-backward-syntactic-ws) - (looking-at "[ \t\n]*:\\([^:]+\\|$\\)"))) + (c-backward-syntactic-ws lim) + (looking-at "\\([ \t\n]\\|\\\\\n\\)*:\\([^:]+\\|$\\)"))) nil (goto-char checkpoint)) ) + (setq checkpoint (point)) (and (eq (char-before) ?\)) - ;; check if we are looking at a method def - (or (not c-method-key) + ;; Check that it isn't a cpp expression, e.g. the + ;; expression of an #if directive or the "function header" + ;; of a #define. + (or (not (c-beginning-of-macro)) + (and (c-forward-to-cpp-define-body) + (< (point) checkpoint))) + ;; check if we are looking at an ObjC method def + (or (not c-opt-method-key) (progn + (goto-char checkpoint) (c-forward-sexp -1) (forward-char -1) - (c-backward-syntactic-ws) + (c-backward-syntactic-ws lim) (not (or (memq (char-before) '(?- ?+)) ;; or a class category (progn @@ -906,118 +1490,234 @@ ))))) ))) -;; defuns to look backwards for things -(defun c-backward-to-start-of-do (&optional lim) - ;; Move to the start of the last "unbalanced" do expression. - ;; Optional LIM is the farthest back to search. If none is found, - ;; nil is returned and point is left unchanged, otherwise t is returned. - (let ((do-level 1) - (case-fold-search nil) - (lim (or lim (c-point 'bod))) - (here (point)) - foundp) - (while (not (zerop do-level)) - ;; we protect this call because trying to execute this when the - ;; while is not associated with a do will throw an error - (condition-case nil - (progn - (c-backward-sexp 1) - (cond - ;; break infloop for illegal C code - ((bobp) (setq do-level 0)) - ((memq (c-in-literal lim) '(c c++))) - ((looking-at "while\\b[^_]") - (setq do-level (1+ do-level))) - ((looking-at "do\\b[^_]") - (if (zerop (setq do-level (1- do-level))) - (setq foundp t))) - ((<= (point) lim) - (setq do-level 0) - (goto-char lim)))) - (error - (goto-char lim) - (setq do-level 0)))) - (if (not foundp) - (goto-char here)) - foundp)) - -(defun c-backward-to-start-of-if (&optional lim) - ;; Move to the start of the last "unbalanced" if and return t. If - ;; none is found, and we are looking at an if clause, nil is - ;; returned. - (let ((if-level 1) - (here (c-point 'bol)) - (case-fold-search nil) - (lim (or (and lim (>= (point) lim) lim) - (c-point 'bod))) - (at-if (looking-at "if\\b[^_]"))) - (catch 'orphan-if - (while (and (not (bobp)) - (not (zerop if-level))) - (c-backward-syntactic-ws) - (condition-case nil - (c-backward-sexp 1) - (error - (unless at-if - (goto-char here) - (c-beginning-of-statement-1) - (setq c-parsing-error - (format "No matching `if' found for `else' on line %d" - (1+ (count-lines (point-min) here)))) - (throw 'orphan-if nil)))) - (cond - ((looking-at "else\\b[^_]") - (setq if-level (1+ if-level))) - ((looking-at "if\\b[^_]") - ;; check for else if... skip over - (let ((here (point))) - (c-safe (c-forward-sexp -1)) - (if (looking-at "\\<else\\>[ \t]+\\<if\\>[^_]") - nil - (setq if-level (1- if-level)) - (goto-char here)))) - ((< (point) lim) - (setq if-level 0) - (goto-char lim)) - )) - t))) +(defun c-in-knr-argdecl (&optional lim) + ;; Return the position of the first argument declaration if point is + ;; inside a K&R style argument declaration list, nil otherwise. + ;; `c-recognize-knr-p' is not checked. If LIM is non-nil, it's a + ;; position that bounds the backward search for the argument list. + ;; + ;; Note: A declaration level context is assumed; the test can return + ;; false positives for statements and #define headers. This test is + ;; even more easily fooled than `c-just-after-func-arglist-p'. + (save-excursion + (save-restriction + ;; Go back to the closest preceding normal parenthesis sexp. We + ;; take that as the argument list in the function header. Then + ;; check that it's followed by some symbol before the next ';' + ;; or '{'. If it does, it's the header of the K&R argdecl we're + ;; in. + (if lim (narrow-to-region lim (point))) + (let (paren-end) + (and (c-safe (setq paren-end (c-down-list-backward (point)))) + (eq (char-after paren-end) ?\)) + (progn + (goto-char (1+ paren-end)) + (c-forward-syntactic-ws) + (looking-at "\\w\\|\\s_")) + (c-safe (c-up-list-backward paren-end)) + (point)))))) (defun c-skip-conditional () ;; skip forward over conditional at point, including any predicate ;; statements in parentheses. No error checking is performed. (c-forward-sexp (cond ;; else if() - ((looking-at "\\<else\\>[ \t]+\\<if\\>\\([^_]\\|$\\)") 3) + ((looking-at (concat "\\<else" + "\\([ \t\n]\\|\\\\\n\\)+" + "if\\>\\([^_]\\|$\\)")) + 3) ;; do, else, try, finally - ((looking-at - "\\<\\(do\\|else\\|try\\|finally\\)\\>\\([^_]\\|$\\)") + ((looking-at (concat "\\<\\(" + "do\\|else\\|try\\|finally" + "\\)\\>\\([^_]\\|$\\)")) 1) ;; for, if, while, switch, catch, synchronized, foreach (t 2)))) -(defun c-beginning-of-closest-statement (&optional lim) - ;; Go back to the closest preceding statement start. +(defun c-after-conditional (&optional lim) + ;; If looking at the token after a conditional then return the + ;; position of its start, otherwise return nil. + (save-excursion + (and (= (c-backward-token-1 1 t lim) 0) + (or (looking-at c-block-stmt-1-key) + (and (eq (char-after) ?\() + (= (c-backward-token-1 1 t lim) 0) + (looking-at c-block-stmt-2-key))) + (point)))) + +(defsubst c-backward-to-block-anchor (&optional lim) + ;; Assuming point is at a brace that opens a statement block of some + ;; kind, move to the proper anchor point for that block. It might + ;; need to be adjusted further by c-add-stmt-syntax, but the + ;; position at return is suitable as start position for that + ;; function. + (unless (= (point) (c-point 'boi)) + (let ((start (c-after-conditional lim))) + (if start + (goto-char start))))) + +(defun c-backward-to-decl-anchor (&optional lim) + ;; Assuming point is at a brace that opens the block of a top level + ;; declaration of some kind, move to the proper anchor point for + ;; that block. + (unless (= (point) (c-point 'boi)) + ;; What we have below is actually an extremely stripped variant of + ;; c-beginning-of-statement-1. + (let ((pos (point))) + ;; Switch syntax table to avoid stopping at line continuations. + (save-restriction + (if lim (narrow-to-region lim (point-max))) + (while (and (progn + (c-backward-syntactic-ws) + (c-safe (goto-char (scan-sexps (point) -1)) t)) + (not (c-crosses-statement-barrier-p (point) pos))) + (setq pos (point))) + (goto-char pos))))) + +(defsubst c-search-decl-header-end () + ;; Search forward for the end of the "header" of the current + ;; declaration. That's the position where the definition body + ;; starts, or the first variable initializer, or the ending + ;; semicolon. I.e. search forward for the closest following + ;; (syntactically relevant) '{', '=' or ';' token. Point is left + ;; _after_ the first found token, or at point-max if none is found. + (c-with-syntax-table (if (c-major-mode-is 'c++-mode) + c++-template-syntax-table + (syntax-table)) + (while (and (c-syntactic-re-search-forward "[;{=]" nil 'move 1 t) + ;; In Pike it can be an operator identifier containing + ;; '='. + (c-major-mode-is 'pike-mode) + (eq (char-before) ?=) + (c-on-identifier))))) + +(defun c-beginning-of-decl-1 (&optional lim) + ;; Go to the beginning of the current declaration, or the beginning + ;; of the previous one if already at the start of it. Point won't + ;; be moved out of any surrounding paren. Return a cons cell on the + ;; form (MOVE . KNR-POS). MOVE is like the return value from + ;; `c-beginning-of-statement-1'. If point skipped over some K&R + ;; style argument declarations (and they are to be recognized) then + ;; KNR-POS is set to the start of the first such argument + ;; declaration, otherwise KNR-POS is nil. If LIM is non-nil, it's a + ;; position that bounds the backward search. + ;; + ;; NB: Cases where the declaration continues after the block, as in + ;; "struct foo { ... } bar;", are currently recognized as two + ;; declarations, e.g. "struct foo { ... }" and "bar;" in this case. + (catch 'return + (let* ((start (point)) + (last-stmt-start (point)) + (move (c-beginning-of-statement-1 lim nil t))) + + (while (and (/= last-stmt-start (point)) + (save-excursion + (c-backward-syntactic-ws lim) + (not (memq (char-before) '(?\; ?} ?: nil))))) + ;; `c-beginning-of-statement-1' stops at a block start, but we + ;; want to continue if the block doesn't begin a top level + ;; construct, i.e. if it isn't preceded by ';', '}', ':', or bob. + (setq last-stmt-start (point) + move (c-beginning-of-statement-1 lim nil t))) + + (when c-recognize-knr-p + (let ((fallback-pos (point)) knr-argdecl-start) + ;; Handle K&R argdecls. Back up after the "statement" jumped + ;; over by `c-beginning-of-statement-1', unless it was the + ;; function body, in which case we're sitting on the opening + ;; brace now. Then test if we're in a K&R argdecl region and + ;; that we started at the other side of the first argdecl in + ;; it. + (unless (eq (char-after) ?{) + (goto-char last-stmt-start)) + (if (and (setq knr-argdecl-start (c-in-knr-argdecl lim)) + (< knr-argdecl-start start) + (progn + (goto-char knr-argdecl-start) + (not (eq (c-beginning-of-statement-1 lim nil t) 'macro)))) + (throw 'return + (cons (if (eq (char-after fallback-pos) ?{) + 'previous + 'same) + knr-argdecl-start)) + (goto-char fallback-pos)))) + + ;; `c-beginning-of-statement-1' counts each brace block as a + ;; separate statement, so the result will be 'previous if we've + ;; moved over any. If they were brace list initializers we might + ;; not have moved over a declaration boundary though, so change it + ;; to 'same if we've moved past a '=' before '{', but not ';'. + ;; (This ought to be integrated into `c-beginning-of-statement-1', + ;; so we avoid this extra pass which potentially can search over a + ;; large amount of text.) + (if (and (eq move 'previous) + (c-with-syntax-table (if (c-major-mode-is 'c++-mode) + c++-template-syntax-table + (syntax-table)) + (save-excursion + (and (c-syntactic-re-search-forward "[;={]" start t 1 t) + (eq (char-before) ?=) + (c-syntactic-re-search-forward "[;{]" start t 1 t) + (eq (char-before) ?{) + (c-safe (goto-char (c-up-list-forward (point))) t) + (not (c-syntactic-re-search-forward ";" start t 1 t)))))) + (cons 'same nil) + (cons move nil))))) + +(defun c-end-of-decl-1 () + ;; Assuming point is at the start of a declaration (as detected by + ;; e.g. `c-beginning-of-decl-1'), go to the end of it. Unlike + ;; `c-beginning-of-decl-1', this function handles the case when a + ;; block is followed by identifiers in e.g. struct declarations in C + ;; or C++. If a proper end was found then t is returned, otherwise + ;; point is moved as far as possible within the current sexp and nil + ;; is returned. This function doesn't handle macros; use + ;; `c-end-of-macro' instead in those cases. (let ((start (point)) - (label-re (concat c-label-key "\\|" - c-switch-label-key)) - stmtbeg) - (if c-access-key - (setq label-re (concat label-re "\\|" c-access-key))) - (c-beginning-of-statement-1 lim) - (while (and (when (<= (point) start) - (setq stmtbeg (point))) - (cond - ((looking-at label-re) - ;; Skip a label. - (goto-char (match-end 0)) - t) - ((looking-at c-conditional-key) - ;; Skip a conditional statement. - (c-safe (c-skip-conditional) t)) - (t nil))) - (c-forward-syntactic-ws start)) - (if stmtbeg - (goto-char stmtbeg)))) + (decl-syntax-table (if (c-major-mode-is 'c++-mode) + c++-template-syntax-table + (syntax-table)))) + (catch 'return + (c-search-decl-header-end) + + (when (and c-recognize-knr-p + (eq (char-before) ?\;) + (c-in-knr-argdecl start)) + ;; Stopped at the ';' in a K&R argdecl section which is + ;; detected using the same criteria as in + ;; `c-beginning-of-decl-1'. Move to the following block + ;; start. + (c-syntactic-re-search-forward "{" nil 'move 1 t)) + + (when (eq (char-before) ?{) + ;; Encountered a block in the declaration. Jump over it. + (condition-case nil + (goto-char (c-up-list-forward (point))) + (goto-char (point-max)) + (throw 'return nil)) + (if (or (not c-opt-block-decls-with-vars-key) + (save-excursion + (c-with-syntax-table decl-syntax-table + (let ((lim (point))) + (goto-char start) + (not (and (c-syntactic-re-search-forward + (concat "[;=\(\[{]\\|\\<\\(" + c-opt-block-decls-with-vars-key + "\\)") + lim t 1 t) + (match-beginning 1) + (not (eq (char-before) ?_)))))))) + ;; The declaration doesn't have any of the + ;; `c-opt-block-decls-with-vars' keywords in the + ;; beginning, so it ends here at the end of the block. + (throw 'return t))) + + (c-with-syntax-table decl-syntax-table + (while (progn + (if (eq (char-before) ?\;) + (throw 'return t)) + (c-syntactic-re-search-forward ";" nil 'move 1 t)))) + nil))) (defun c-beginning-of-member-init-list (&optional limit) ;; Goes to the beginning of a member init list (i.e. just after the @@ -1055,61 +1755,26 @@ (and (< limit (point)) (eq (char-before) ?:))) -(defun c-skip-case-statement-forward (state &optional lim) - ;; skip forward over case/default bodies, with optional maximal - ;; limit. if no next case body is found, nil is returned and point - ;; is not moved - (let ((lim (or lim (point-max))) - (here (point)) - donep foundp bufpos - (safepos (point)) - (balanced (car state))) - ;; search until we've passed the limit, or we've found our match - (while (and (< (point) lim) - (not donep)) - (setq safepos (point)) - ;; see if we can find a case statement, not in a literal - (if (and (re-search-forward c-switch-label-key lim 'move) - (setq bufpos (match-beginning 0)) - (not (c-in-literal safepos)) - (/= bufpos here)) - ;; if we crossed into a balanced sexp, we know the case is - ;; not part of our switch statement, so just bound over the - ;; sexp and keep looking. - (if (and (consp balanced) - (> bufpos (car balanced)) - (< bufpos (cdr balanced))) - (goto-char (cdr balanced)) - (goto-char bufpos) - (setq donep t - foundp t)))) - (if (not foundp) - (goto-char here)) - foundp)) - -(defun c-search-uplist-for-classkey (brace-state) +(defun c-search-uplist-for-classkey (paren-state) ;; search for the containing class, returning a 2 element vector if ;; found. aref 0 contains the bufpos of the boi of the class key ;; line, and aref 1 contains the bufpos of the open brace. - (if (null brace-state) - ;; no brace-state means we cannot be inside a class + (if (null paren-state) + ;; no paren-state means we cannot be inside a class nil - (let ((carcache (car brace-state)) + (let ((carcache (car paren-state)) search-start search-end) (if (consp carcache) ;; a cons cell in the first element means that there is some ;; balanced sexp before the current bufpos. this we can ;; ignore. the nth 1 and nth 2 elements define for us the ;; search boundaries - (setq search-start (nth 2 brace-state) - search-end (nth 1 brace-state)) + (setq search-start (nth 2 paren-state) + search-end (nth 1 paren-state)) ;; if the car was not a cons cell then nth 0 and nth 1 define ;; for us the search boundaries - (setq search-start (nth 1 brace-state) - search-end (nth 0 brace-state))) - ;; search-end cannot be a cons cell - (and (consp search-end) - (error "consp search-end: %s" search-end)) + (setq search-start (nth 1 paren-state) + search-end (nth 0 paren-state))) ;; if search-end is nil, or if the search-end character isn't an ;; open brace, we are definitely not in a class (if (or (not search-end) @@ -1132,22 +1797,18 @@ (save-excursion (save-restriction (goto-char search-start) - (let ((search-key (concat c-class-key "\\|" c-extra-toplevel-key)) - foundp class match-end) - (if c-inexpr-class-key - (setq search-key (concat search-key "\\|" - c-inexpr-class-key))) + (let (foundp class match-end) (while (and (not foundp) (progn - (c-forward-syntactic-ws) + (c-forward-syntactic-ws search-end) (> search-end (point))) - (re-search-forward search-key search-end t)) + (re-search-forward c-decl-block-key search-end t)) (setq class (match-beginning 0) match-end (match-end 0)) + (goto-char class) (if (c-in-literal search-start) - nil ; its in a comment or string, ignore - (goto-char class) - (skip-chars-forward " \t\n") + (goto-char match-end) ; its in a comment or string, ignore + (c-skip-ws-forward) (setq foundp (vector (c-point 'boi) search-end)) (cond ;; check for embedded keywords @@ -1157,15 +1818,14 @@ (goto-char match-end) (setq foundp nil)) ;; make sure we're really looking at the start of a - ;; class definition, and not a forward decl, return - ;; arg, template arg list, or an ObjC or Java method. - ((and c-method-key - (re-search-forward c-method-key search-end t) + ;; class definition, and not an ObjC method. + ((and c-opt-method-key + (re-search-forward c-opt-method-key search-end t) (not (c-in-literal class))) (setq foundp nil)) ;; Check if this is an anonymous inner class. - ((and c-inexpr-class-key - (looking-at c-inexpr-class-key)) + ((and c-opt-inexpr-class-key + (looking-at c-opt-inexpr-class-key)) (while (and (= (c-forward-token-1 1 t) 0) (looking-at "(\\|\\w\\|\\s_\\|\\."))) (if (eq (point) search-end) @@ -1198,7 +1858,7 @@ foundp)) ))))) -(defun c-inside-bracelist-p (containing-sexp brace-state) +(defun c-inside-bracelist-p (containing-sexp paren-state) ;; return the buffer position of the beginning of the brace list ;; statement if we're inside a brace list, otherwise return nil. ;; CONTAINING-SEXP is the buffer pos of the innermost containing @@ -1215,10 +1875,10 @@ (goto-char containing-sexp) (c-forward-sexp -1) (let (bracepos) - (if (and (or (looking-at "enum[\t\n ]+") + (if (and (or (looking-at "enum\\>[^_]") (progn (c-forward-sexp -1) - (looking-at "enum[\t\n ]+"))) - (setq bracepos (c-safe (scan-lists (point) 1 -1))) + (looking-at "enum\\>[^_]"))) + (setq bracepos (c-down-list-forward (point))) (not (c-crosses-statement-barrier-p (point) (- bracepos 2)))) (point))))) @@ -1228,24 +1888,28 @@ ;; Pike can have class definitions anywhere, so we must ;; check for the class key here. (and (c-major-mode-is 'pike-mode) - (concat c-class-key "\\|" c-extra-toplevel-key))) - bufpos lim braceassignp) + c-decl-block-key)) + bufpos braceassignp lim next-containing) (while (and (not bufpos) containing-sexp) - (if (consp containing-sexp) - (setq containing-sexp (car brace-state) - brace-state (cdr brace-state)) + (when paren-state + (if (consp (car paren-state)) + (setq lim (cdr (car paren-state)) + paren-state (cdr paren-state)) + (setq lim (car paren-state))) + (when paren-state + (setq next-containing (car paren-state) + paren-state (cdr paren-state)))) (goto-char containing-sexp) - (if (c-looking-at-inexpr-block) - ;; We're in an in-expression block of some kind. Do - ;; not check nesting. + (if (c-looking-at-inexpr-block next-containing next-containing) + ;; We're in an in-expression block of some kind. Do not + ;; check nesting. We deliberately set the limit to the + ;; containing sexp, so that c-looking-at-inexpr-block + ;; doesn't check for an identifier before it. (setq containing-sexp nil) ;; see if the open brace is preceded by = or [...] in ;; this statement, but watch out for operator= - (setq lim (if (consp (car brace-state)) - (cdr (car brace-state)) - (car brace-state)) - braceassignp 'dontknow) + (setq braceassignp 'dontknow) (c-backward-token-1 1 t lim) ;; Checks to do only on the first sexp before the brace. (when (and (c-major-mode-is 'java-mode) @@ -1280,7 +1944,7 @@ (setq braceassignp (cond ;; Check for operator = - ((looking-at "operator\\>") nil) + ((looking-at "operator\\>[^_]") nil) ;; Check for `<opchar>= in Pike. ((and (c-major-mode-is 'pike-mode) (or (eq (char-after) ?`) @@ -1293,17 +1957,18 @@ ((looking-at "\\s.") 'maybe) ;; make sure we're not in a C++ template ;; argument assignment - ((and (c-major-mode-is 'c++-mode) - (save-excursion - (let ((here (point)) - (pos< (progn - (skip-chars-backward "^<>") - (point)))) - (and (eq (char-before) ?<) - (not (c-crosses-statement-barrier-p - pos< here)) - (not (c-in-literal)) - )))) + ((and + (c-major-mode-is 'c++-mode) + (save-excursion + (let ((here (point)) + (pos< (progn + (skip-chars-backward "^<>") + (point)))) + (and (eq (char-before) ?<) + (not (c-crosses-statement-barrier-p + pos< here)) + (not (c-in-literal)) + )))) nil) (t t)))))) (if (and (eq braceassignp 'dontknow) @@ -1313,15 +1978,15 @@ (if (eq (char-after) ?\;) ;; Brace lists can't contain a semicolon, so we're done. (setq containing-sexp nil) - ;; lets see if we're nested. find the most nested - ;; containing brace - (setq containing-sexp (car brace-state) - brace-state (cdr brace-state))) + ;; Go up one level. + (setq containing-sexp next-containing + lim nil + next-containing nil)) ;; we've hit the beginning of the aggregate list (c-beginning-of-statement-1 - (c-most-enclosing-brace brace-state)) + (c-most-enclosing-brace paren-state)) (setq bufpos (point)))) - )) + ) bufpos)) )) @@ -1375,82 +2040,126 @@ (cons (list beg) type))))) (error nil)))) -(defun c-looking-at-bos () - ;; Returns nil if inside a statement or declaration. +(defun c-looking-at-bos (&optional lim) + ;; Return non-nil if between two statements or declarations, assuming + ;; point is not inside a literal or comment. (save-excursion - (c-backward-syntactic-ws) + (c-backward-syntactic-ws lim) (or (bobp) - (memq (char-before) '(?\; ?})) + ;; Return t if at the start inside some parenthesis expression + ;; too, to catch macros that have statements as arguments. + (memq (char-before) '(?\; ?} ?\()) (and (eq (char-before) ?{) (not (and c-special-brace-lists (progn (backward-char) (c-looking-at-special-brace-list)))))))) -(defun c-looking-at-inexpr-block (&optional lim) +(defun c-looking-at-inexpr-block (lim containing-sexp) ;; Returns non-nil if we're looking at the beginning of a block ;; inside an expression. The value returned is actually a cons of ;; either 'inlambda, 'inexpr-statement or 'inexpr-class and the ;; position of the beginning of the construct. LIM limits the - ;; backward search. + ;; backward search. CONTAINING-SEXP is the start position of the + ;; closest containing list. If it's nil, the containing paren isn't + ;; used to decide whether we're inside an expression or not. If + ;; both LIM and CONTAINING-SEXP is used, LIM needs to be farther + ;; back. (save-excursion - (or lim (setq lim (point-min))) - (let ((block-follows (eq (char-after) ?{))) - ;; Look at the character after point only as a last resort when - ;; we can't disambiguate. - (if (and block-follows - (progn (c-backward-syntactic-ws) (> (point) lim)) - (eq (char-before) ?\() - (not (and c-special-brace-lists - (c-looking-at-special-brace-list)))) - (cons 'inexpr-statement (point)) - (let (res) - (while (and (not res) - (= (c-backward-token-1 1 t lim) 0) - (>= (point) lim) - (looking-at "(\\|\\w\\|\\s_\\|\\.")) - (setq res - (cond ((and block-follows - c-inexpr-class-key - (looking-at c-inexpr-class-key) - (or (not (looking-at c-class-key)) - (let ((prev (point))) - (while (and (= (c-backward-token-1 1 t lim) - 0) - (>= (point) lim) - (eq (char-syntax (char-after)) - ?w)) - (setq prev (point))) - (goto-char prev) - (not (c-looking-at-bos))))) - (cons 'inexpr-class (point))) - ((and c-inexpr-block-key - (looking-at c-inexpr-block-key)) - (cons 'inexpr-statement (point))) - ((and c-lambda-key - (looking-at c-lambda-key)) - (cons 'inlambda (point)))))) - res))))) + (let ((res 'maybe) passed-bracket + (closest-lim (or containing-sexp lim (point-min))) + ;; Look at the character after point only as a last resort + ;; when we can't disambiguate. + (block-follows (and (eq (char-after) ?{) (point)))) + (while (and (eq res 'maybe) + (progn (c-backward-syntactic-ws) + (> (point) closest-lim)) + (not (bobp)) + (progn (backward-char) + (looking-at "[\]\).]\\|\\w\\|\\s_")) + (progn (forward-char) + (goto-char (scan-sexps (point) -1)))) + (setq res + (cond + ((and block-follows + c-opt-inexpr-class-key + (looking-at c-opt-inexpr-class-key)) + (and (not passed-bracket) + (or (not (looking-at c-class-key)) + ;; If the class definition is at the start of + ;; a statement, we don't consider it an + ;; in-expression class. + (let ((prev (point))) + (while (and + (= (c-backward-token-1 1 nil closest-lim) 0) + (eq (char-syntax (char-after)) ?w)) + (setq prev (point))) + (goto-char prev) + (not (c-looking-at-bos))) + ;; Also, in Pike we treat it as an + ;; in-expression class if it's used in an + ;; object clone expression. + (save-excursion + (and (c-major-mode-is 'pike-mode) + (progn (goto-char block-follows) + (= (c-forward-token-1 1 t) 0)) + (eq (char-after) ?\()))) + (cons 'inexpr-class (point)))) + ((and c-opt-inexpr-block-key + (looking-at c-opt-inexpr-block-key)) + (cons 'inexpr-statement (point))) + ((and c-opt-lambda-key + (looking-at c-opt-lambda-key)) + (cons 'inlambda (point))) + ((and c-opt-block-stmt-key + (looking-at c-opt-block-stmt-key)) + nil) + (t + (if (eq (char-after) ?\[) + (setq passed-bracket t)) + 'maybe)))) + (if (eq res 'maybe) + (when (and block-follows + containing-sexp + (eq (char-after containing-sexp) ?\()) + (goto-char containing-sexp) + (if (or (save-excursion + (c-backward-syntactic-ws lim) + (and (> (point) (or lim (point-min))) + (c-on-identifier))) + (and c-special-brace-lists + (c-looking-at-special-brace-list))) + nil + (cons 'inexpr-statement (point)))) + res)))) -(defun c-looking-at-inexpr-block-backward (&optional lim) +(defun c-looking-at-inexpr-block-backward (paren-state) ;; Returns non-nil if we're looking at the end of an in-expression ;; block, otherwise the same as `c-looking-at-inexpr-block'. + ;; PAREN-STATE is the paren state relevant at the current position. (save-excursion - (let ((lim (or lim (c-point 'bod)))) - (c-safe - (c-backward-syntactic-ws lim) - (if (eq (char-before) ?}) ; Recognize only a block currently. - (progn - (c-forward-sexp -1) - (if (>= (point) lim) - (c-looking-at-inexpr-block lim)))))))) + ;; We currently only recognize a block. + (let ((here (point)) + (elem (car-safe paren-state)) + containing-sexp) + (when (and (consp elem) + (progn (goto-char (cdr elem)) + (c-forward-syntactic-ws here) + (= (point) here))) + (goto-char (car elem)) + (if (setq paren-state (cdr paren-state)) + (setq containing-sexp (car-safe paren-state))) + (c-looking-at-inexpr-block (c-safe-position containing-sexp + paren-state) + containing-sexp))))) (defun c-on-identifier () - ;; Returns non-nil if we're on or directly after an identifier. + "Return non-nil if we're on or directly after an identifier. +Keywords are recognized and not considered identifiers." (if (or (memq (char-syntax (or (char-after) ? )) '(?w ?_)) (memq (char-syntax (or (char-before) ? )) '(?w ?_))) (save-excursion (skip-syntax-backward "w_") - (not (looking-at c-keywords))) + (not (looking-at c-keywords-regexp))) (if (c-major-mode-is 'pike-mode) ;; Handle the `<operator> syntax in Pike. (save-excursion @@ -1467,49 +2176,73 @@ (looking-at "[-!%&*+/<=>^|~]\\|()\\|\\[]")))))) -(defun c-most-enclosing-brace (state) - ;; return the bufpos of the most enclosing brace that hasn't been - ;; narrowed out by any enclosing class, or nil if none was found +(defun c-most-enclosing-brace (paren-state &optional bufpos) + ;; Return the bufpos of the innermost enclosing brace before bufpos + ;; that hasn't been narrowed out, or nil if none was found. (let (enclosingp) - (while (and state (not enclosingp)) - (setq enclosingp (car state) - state (cdr state)) - (if (consp enclosingp) + (or bufpos (setq bufpos 134217727)) + (while paren-state + (setq enclosingp (car paren-state) + paren-state (cdr paren-state)) + (if (or (consp enclosingp) + (>= enclosingp bufpos)) (setq enclosingp nil) - (if (> (point-min) enclosingp) + (if (< enclosingp (point-min)) (setq enclosingp nil)) - (setq state nil))) + (setq paren-state nil))) enclosingp)) -(defun c-least-enclosing-brace (state) - ;; return the bufpos of the least (highest) enclosing brace that - ;; hasn't been narrowed out by any enclosing class, or nil if none - ;; was found. - (c-most-enclosing-brace (nreverse state))) +(defun c-least-enclosing-brace (paren-state &optional bufpos) + ;; Return the bufpos of the outermost enclosing brace before bufpos + ;; that hasn't been narrowed out, or nil if none was found. + (let (pos elem) + (or bufpos (setq bufpos 134217727)) + (while paren-state + (setq elem (car paren-state) + paren-state (cdr paren-state)) + (unless (or (consp elem) + (>= elem bufpos)) + (if (>= elem (point-min)) + (setq pos elem)))) + pos)) -(defun c-safe-position (bufpos state) - ;; return the closest known safe position higher up than point - (let ((safepos nil)) - (while state - (setq safepos - (if (consp (car state)) - (cdr (car state)) - (car state))) - (if (< safepos bufpos) - (setq state nil) - (setq state (cdr state)))) - safepos)) +(defun c-safe-position (bufpos paren-state) + ;; Return the closest known safe position higher up than BUFPOS, or + ;; nil if PAREN-STATE doesn't contain one. Return nil if BUFPOS is + ;; nil, which is useful to find the closest limit before a given + ;; limit that might be nil. + (when bufpos + (let ((c-macro-start (c-query-macro-start)) safepos) + (if (and c-macro-start + (< c-macro-start bufpos)) + ;; Make sure bufpos is outside the macro we might be in. + (setq bufpos c-macro-start)) + (catch 'done + (while paren-state + (setq safepos + (if (consp (car paren-state)) + (cdr (car paren-state)) + (car paren-state))) + (if (< safepos bufpos) + (throw 'done safepos) + (setq paren-state (cdr paren-state)))) + (if (eq c-macro-start bufpos) + ;; Backed up bufpos to the macro start and got outside the + ;; state. We know the macro is at the top level in this case, + ;; so we can use the macro start as the safe position. + c-macro-start))))) -(defun c-narrow-out-enclosing-class (state lim) - ;; narrow the buffer so that the enclosing class is hidden - (setq state (c-whack-state (point) state)) +(defun c-narrow-out-enclosing-class (paren-state lim) + ;; Narrow the buffer so that the enclosing class is hidden. Uses + ;; and returns the value from c-search-uplist-for-classkey. + (setq paren-state (c-whack-state-after (point) paren-state)) (let (inclass-p) - (and state - (setq inclass-p (c-search-uplist-for-classkey state)) + (and paren-state + (setq inclass-p (c-search-uplist-for-classkey paren-state)) (narrow-to-region (progn (goto-char (1+ (aref inclass-p 1))) - (skip-chars-forward " \t\n" lim) + (c-skip-ws-forward lim) ;; if point is now left of the class opening brace, we're ;; hosed, so try a different tact (if (<= (point) (aref inclass-p 1)) @@ -1525,24 +2258,305 @@ inclass-p)) -;; This function implements the main decision tree for determining the -;; syntactic analysis of the current line of code. Yes, it's huge and -;; bloated! +;; c-guess-basic-syntax implements the main decision tree for +;; determining the syntactic analysis of the current line of code. +;; Yes, it's huge and bloated! + +;; It's useful to break out some parts of the decision tree to +;; separate functions, which are all collected below. Use dynamic +;; binding to propagate back the syntax results from them. +(defvar syntax) +(defvar syntactic-relpos) + +(defun c-add-stmt-syntax (syntax-symbol + stop-at-boi-only + containing-sexp + paren-state + &optional at-block-start) + ;; Do the generic processing to anchor the given syntax symbol on + ;; the preceding statement: Skip over any labels and containing + ;; statements on the same line, and then search backward until we + ;; find a statement or block start that begins at boi without a + ;; label or comment. + ;; + ;; Point is assumed to be at the prospective anchor point for the + ;; given SYNTAX-SYMBOL. More syntax entries are added if we need to + ;; skip past block opens and containing statement. All the added + ;; syntax elements will get the same anchor point. + ;; + ;; If STOP-AT-BOI-ONLY is nil, we might stop in the middle of the + ;; line if another statement precedes the current one on this line. + ;; + ;; If AT-BLOCK-START is non-nil, point is taken to be at the + ;; beginning of a block or brace list, which then might be nested + ;; inside an expression. If AT-BLOCK-START is nil, this is found + ;; out by checking whether the character at point is "{" or not. + (if (= (point) (c-point 'boi)) + ;; This is by far the most common case, so let's give it special + ;; treatment. + (c-add-syntax syntax-symbol (point)) + + (let* ((savepos (point)) + (syms (list syntax-symbol)) + (syms-tail syms) + (boi (c-point 'boi)) + (prev-paren (if at-block-start ?{ (char-after))) + step-type step-tmp at-comment add-inexpr-stmt) + + ;; Begin by skipping any labels and containing statements that + ;; are on the same line. + (while (and (/= (point) boi) + (if (memq (setq step-tmp + (c-beginning-of-statement-1 boi nil t)) + '(up label)) + t + (goto-char savepos) + nil) + (/= (point) savepos)) + (setq savepos (point) + step-type step-tmp)) + + ;; Skip over any comments that stands between the statement and + ;; boi. If stop-at-boi-only is nil and we're not at boi after + ;; this, then we're done. + (while (and (/= (setq savepos (point)) boi) + (c-forward-comment -1)) + (setq at-comment t + boi (c-point 'boi))) + (goto-char savepos) + + (when (or stop-at-boi-only + (= (point) boi)) + (catch 'done + ;; Loop if we have to back out of the containing block. + (while + (progn + ;; Loop if we have to back up another statement. + (while + (progn + ;; Always start by skipping over any comments that + ;; stands between the statement and boi. + (while (and (/= (setq savepos (point)) boi) + (c-forward-comment -1)) + (setq at-comment t + boi (c-point 'boi))) + (goto-char savepos) + (and (or at-comment + (eq step-type 'label) + (/= savepos boi)) + (progn + ;; Current position not good enough; skip + ;; backward another statement. + (setq stop-at-boi-only t + step-type (c-beginning-of-statement-1 + containing-sexp)) + ;; Record this a substatement if we skipped + ;; up one level, but not if we're still on + ;; the same line. This so e.g. a sequence + ;; of "else if" clauses won't indent deeper + ;; and deeper. + (when (and (eq step-type 'up) + (< (point) boi)) + (setcdr syms-tail (list 'substatement)) + (setq syms-tail (cdr syms-tail))) + (setq boi (c-point 'boi)) + (/= (point) savepos)))) + (setq savepos (point) + at-comment nil)) + (setq at-comment nil) + + (when (and (eq step-type 'same) + containing-sexp) + (goto-char containing-sexp) + (setq paren-state (c-whack-state-after containing-sexp + paren-state) + containing-sexp (c-most-enclosing-brace paren-state)) + + (when (and (prog1 + (eq prev-paren ?{) + (setq prev-paren (char-after))) + (eq prev-paren ?\()) + (c-backward-syntactic-ws containing-sexp) + (when (c-on-identifier) + ;; Arrived at a function arglist start. Exit with + ;; the position of the first argument inside it. + (goto-char savepos) + (throw 'done t)) + ;; We're in an in-expression statement. Remember + ;; this. We'll iterate below, but won't add any + ;; syntax element. + (setq add-inexpr-stmt t)) + + (setq savepos (point) + boi (c-point 'boi) + step-type (c-beginning-of-statement-1 containing-sexp)) + + (let ((at-bod (and (eq step-type 'same) + (/= savepos (point)) + (eq prev-paren ?{)))) + (when (= savepos boi) + ;; If the open brace was at boi, we're always + ;; done. The c-beginning-of-statement-1 call + ;; above is necessary anyway, to decide the type + ;; of block-intro to add. + (goto-char savepos) + (setq savepos nil)) + + (when (eq prev-paren ?{) + (setcdr syms-tail (list (if at-bod + 'defun-block-intro + 'statement-block-intro))) + (setq syms-tail (cdr syms-tail))) + + (when (and (not at-bod) savepos) + ;; Loop if the brace wasn't at boi, and we didn't + ;; arrive at a defun block. + (if (eq step-type 'same) + ;; Avoid backing up another sexp if the point + ;; we're at now is found to be good enough in + ;; the loop above. + (setq step-type nil)) + (setq stop-at-boi-only t + boi (c-point 'boi))))) + )))) + + (while syms + (c-add-syntax (car syms) (point)) + (setq syms (cdr syms))) + (if add-inexpr-stmt + (c-add-syntax 'inexpr-statement)) + ))) + +(defun c-add-class-syntax (symbol classkey paren-state) + ;; The inclass and class-close syntactic symbols are added in + ;; several places and some work is needed to fix everything. + ;; Therefore it's collected here. + (save-restriction + (widen) + (let (inexpr anchor containing-sexp) + (goto-char (aref classkey 1)) + (if (and (eq symbol 'inclass) (= (point) (c-point 'boi))) + (c-add-syntax symbol (setq anchor (point))) + (c-add-syntax symbol (setq anchor (aref classkey 0))) + (if (and c-opt-inexpr-class-key + (setq containing-sexp (c-most-enclosing-brace paren-state + (point)) + inexpr (cdr (c-looking-at-inexpr-block + (c-safe-position containing-sexp + paren-state) + containing-sexp))) + (/= inexpr (c-point 'boi inexpr))) + (c-add-syntax 'inexpr-class))) + anchor))) + +(defun c-guess-continued-construct (indent-point + char-after-ip + beg-of-same-or-containing-stmt + containing-sexp + paren-state) + ;; This function contains the decision tree reached through both + ;; cases 18 and 10. It's a continued statement or top level + ;; construct of some kind. + (let (special-brace-list) + (goto-char indent-point) + (skip-chars-forward " \t") + (cond + ;; (CASE A removed.) + ;; CASE B: open braces for class or brace-lists + ((setq special-brace-list + (or (and c-special-brace-lists + (c-looking-at-special-brace-list)) + (eq char-after-ip ?{))) + (cond + ;; CASE B.1: class-open + ((save-excursion + (goto-char indent-point) + (skip-chars-forward " \t{") + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) + (and decl + (setq beg-of-same-or-containing-stmt (aref decl 0))) + )) + (c-add-syntax 'class-open beg-of-same-or-containing-stmt)) + ;; CASE B.2: brace-list-open + ((or (consp special-brace-list) + (save-excursion + (goto-char beg-of-same-or-containing-stmt) + (looking-at "enum\\>[^_]")) + (save-excursion + (goto-char indent-point) + (while (and (> (point) beg-of-same-or-containing-stmt) + (= (c-backward-token-1 1 t) 0) + (/= (char-after) ?=))) + (eq (char-after) ?=))) + ;; The most semantically accurate symbol here is + ;; brace-list-open, but we report it simply as a statement-cont. + ;; The reason is that one normally adjusts brace-list-open for + ;; brace lists as top-level constructs, and brace lists inside + ;; statements is a completely different context. + (c-beginning-of-statement-1 containing-sexp) + (c-add-stmt-syntax 'statement-cont nil containing-sexp paren-state)) + ;; CASE B.3: The body of a function declared inside a normal + ;; block. Can occur e.g. in Pike and when using gcc + ;; extensions. Might also trigger it with some macros followed + ;; by blocks, and this gives sane indentation then too. + ;; C.f. cases 16F and 17G. + ((progn + (goto-char indent-point) + (and (not (c-looking-at-bos)) + (eq (c-beginning-of-statement-1 containing-sexp nil nil t) + 'same))) + (c-add-stmt-syntax 'defun-open t containing-sexp paren-state)) + ;; CASE B.4: Continued statement with block open. + (t + (goto-char beg-of-same-or-containing-stmt) + (c-add-stmt-syntax 'statement-cont nil containing-sexp paren-state) + (c-add-syntax 'block-open)) + )) + ;; CASE C: iostream insertion or extraction operator + ((and (looking-at "<<\\|>>") + (save-excursion + (goto-char beg-of-same-or-containing-stmt) + (while (and (re-search-forward "<<\\|>>" indent-point 'move) + (c-in-literal beg-of-same-or-containing-stmt))) + ;; if we ended up at indent-point, then the first streamop is on a + ;; separate line. Indent the line like a statement-cont instead + (when (/= (point) indent-point) + (c-add-syntax 'stream-op (c-point 'boi)) + t)))) + ;; CASE D: continued statement. + (t + (c-beginning-of-statement-1 containing-sexp) + (c-add-stmt-syntax 'statement-cont nil containing-sexp paren-state)) + ))) (defun c-guess-basic-syntax () + "Return the syntactic context of the current line." (save-excursion (save-restriction (beginning-of-line) (let* ((indent-point (point)) (case-fold-search nil) - (fullstate (c-parse-state)) - (state fullstate) + (paren-state (c-parse-state)) literal containing-sexp char-before-ip char-after-ip lim - syntax placeholder c-in-literal-cache inswitch-p + syntax placeholder c-in-literal-cache step-type tmpsymbol keyword injava-inher special-brace-list ;; narrow out any enclosing class or extern "C" block - (inclass-p (c-narrow-out-enclosing-class state indent-point)) - inenclosing-p) + (inclass-p (c-narrow-out-enclosing-class paren-state + indent-point)) + ;; c-state-cache is shadowed here. That means we must + ;; not do any changes during the execution of this + ;; function, since c-check-state-cache then would change + ;; this local variable and leave a bogus value in the + ;; global one. + (c-state-cache (if inclass-p + (c-whack-state-before (point-min) paren-state) + paren-state)) + (c-state-cache-start (point-min)) + inenclosing-p macro-start in-macro-expr + ;; There's always at most one syntactic element which got + ;; a relpos. It's stored in syntactic-relpos. + syntactic-relpos + (c-stmt-delim-chars c-stmt-delim-chars)) ;; check for meta top-level enclosing constructs, possible ;; extern language definitions, possibly (in C++) namespace ;; definitions. @@ -1552,7 +2566,7 @@ (if (and inclass-p (progn (goto-char (aref inclass-p 0)) - (looking-at (concat c-extra-toplevel-key "[^_]")))) + (looking-at c-other-decl-block-key))) (let ((enclosing (match-string 1))) (cond ((string-equal enclosing "extern") @@ -1560,45 +2574,62 @@ ((string-equal enclosing "namespace") (setq inenclosing-p 'namespace)) ))))) - ;; get the buffer position of the most nested opening brace, - ;; if there is one, and it hasn't been narrowed out - (save-excursion - (goto-char indent-point) - (skip-chars-forward " \t}") - (skip-chars-backward " \t") - (while (and state - (not containing-sexp)) - (setq containing-sexp (car state) - state (cdr state)) - (if (consp containing-sexp) - ;; if cdr == point, then containing sexp is the brace - ;; that opens the sexp we close - (if (= (cdr containing-sexp) (point)) - (setq containing-sexp (car containing-sexp)) - ;; otherwise, ignore this element - (setq containing-sexp nil)) - ;; ignore the bufpos if its been narrowed out by the - ;; containing class or does not contain the indent point - (if (or (<= containing-sexp (point-min)) - (>= containing-sexp indent-point)) - (setq containing-sexp nil))))) - ;; set the limit on the farthest back we need to search - (setq lim (or containing-sexp - (if (consp (car fullstate)) - (cdr (car fullstate)) - nil) - (point-min))) + ;; Init some position variables: + ;; + ;; containing-sexp is the open paren of the closest + ;; surrounding sexp or nil if there is none that hasn't been + ;; narrowed out. + ;; + ;; lim is the position after the closest preceding brace sexp + ;; (nested sexps are ignored), or the position after + ;; containing-sexp if there is none, or (point-min) if + ;; containing-sexp is nil. + ;; + ;; c-state-cache is the state from c-parse-state at + ;; indent-point, without any parens outside the region + ;; narrowed by c-narrow-out-enclosing-class. + ;; + ;; paren-state is the state from c-parse-state outside + ;; containing-sexp, or at indent-point if containing-sexp is + ;; nil. paren-state is not limited to the narrowed region, as + ;; opposed to c-state-cache. + (if c-state-cache + (progn + (setq containing-sexp (car paren-state) + paren-state (cdr paren-state)) + (if (consp containing-sexp) + (progn + (setq lim (cdr containing-sexp)) + (if (cdr c-state-cache) + ;; Ignore balanced paren. The next entry + ;; can't be another one. + (setq containing-sexp (car (cdr c-state-cache)) + paren-state (cdr paren-state)) + ;; If there is no surrounding open paren then + ;; put the last balanced pair back on paren-state. + (setq paren-state (cons containing-sexp paren-state) + containing-sexp nil))) + (setq lim (1+ containing-sexp)))) + (setq lim (point-min))) + + ;; If we're in a parenthesis list then ',' delimits the + ;; "statements" rather than being an operator (with the + ;; exception of the "for" clause). This difference is + ;; typically only noticeable when statements are used in macro + ;; arglists. + (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) - (skip-chars-forward " \t") - (setq char-after-ip (char-after)) (c-backward-syntactic-ws lim) (setq char-before-ip (char-before)) (goto-char indent-point) (skip-chars-forward " \t") + (setq char-after-ip (char-after)) ;; are we in a literal? (setq literal (c-in-literal lim)) @@ -1606,21 +2637,142 @@ ;; now figure out syntactic qualities of the current line (cond ;; CASE 1: in a string. - ((memq literal '(string)) + ((eq literal 'string) (c-add-syntax 'string (c-point 'bopl))) ;; CASE 2: in a C or C++ style comment. ((memq literal '(c c++)) (c-add-syntax literal (car (c-literal-limits lim)))) ;; CASE 3: in a cpp preprocessor macro continuation. - ((and (eq literal 'pound) - (/= (save-excursion - (c-beginning-of-macro lim) - (setq placeholder (point))) - (c-point 'boi))) - (c-add-syntax 'cpp-macro-cont placeholder)) - ;; CASE 4: In-expression statement. - ((and (or c-inexpr-class-key c-inexpr-block-key c-lambda-key) - (setq placeholder (c-looking-at-inexpr-block))) + ((and (save-excursion + (when (c-beginning-of-macro) + (setq macro-start (point)))) + (/= macro-start (c-point 'boi)) + (progn + (setq tmpsymbol 'cpp-macro-cont) + (or (not c-syntactic-indentation-in-macros) + (save-excursion + (goto-char macro-start) + ;; If at the beginning of the body of a #define + ;; directive then analyze as cpp-define-intro + ;; only. Go on with the syntactic analysis + ;; otherwise. in-macro-expr is set if we're in a + ;; cpp expression, i.e. before the #define body + ;; or anywhere in a non-#define directive. + (if (c-forward-to-cpp-define-body) + (let ((indent-boi (c-point 'boi indent-point))) + (setq in-macro-expr (> (point) indent-boi) + tmpsymbol 'cpp-define-intro) + (= (point) indent-boi)) + (setq in-macro-expr t) + nil))))) + (c-add-syntax tmpsymbol macro-start) + (setq macro-start nil)) + ;; CASE 11: an else clause? + ((looking-at "else\\>[^_]") + (c-beginning-of-statement-1 containing-sexp) + (c-add-stmt-syntax 'else-clause t containing-sexp paren-state)) + ;; CASE 12: while closure of a do/while construct? + ((and (looking-at "while\\>[^_]") + (save-excursion + (prog1 (eq (c-beginning-of-statement-1 containing-sexp) + 'beginning) + (setq placeholder (point))))) + (goto-char placeholder) + (c-add-stmt-syntax 'do-while-closure t containing-sexp paren-state)) + ;; CASE 13: A catch or finally clause? This case is simpler + ;; than if-else and do-while, because a block is required + ;; after every try, catch and finally. + ((save-excursion + (and (cond ((c-major-mode-is 'c++-mode) + (looking-at "catch\\>[^_]")) + ((c-major-mode-is 'java-mode) + (looking-at "\\(catch\\|finally\\)\\>[^_]"))) + (and (c-safe (c-backward-syntactic-ws) + (c-backward-sexp) + t) + (eq (char-after) ?{) + (c-safe (c-backward-syntactic-ws) + (c-backward-sexp) + t) + (if (eq (char-after) ?\() + (c-safe (c-backward-sexp) t) + t)) + (looking-at "\\(try\\|catch\\)\\>[^_]") + (setq placeholder (point)))) + (goto-char placeholder) + (c-add-stmt-syntax 'catch-clause t containing-sexp paren-state)) + ;; CASE 18: A substatement we can recognize by keyword. + ((save-excursion + (and c-opt-block-stmt-key + (not (eq char-before-ip ?\;)) + (not (memq char-after-ip '(?\) ?\] ?,))) + (or (not (eq char-before-ip ?})) + (c-looking-at-inexpr-block-backward c-state-cache)) + (> (point) + (progn + ;; Ought to cache the result from the + ;; c-beginning-of-statement-1 calls here. + (setq placeholder (point)) + (while (eq (setq step-type + (c-beginning-of-statement-1 lim)) + 'label)) + (if (eq step-type 'previous) + (goto-char placeholder) + (setq placeholder (point)) + (if (and (eq step-type 'same) + (not (looking-at c-opt-block-stmt-key))) + ;; Step up to the containing statement if we + ;; stayed in the same one. + (let (step) + (while (eq + (setq step + (c-beginning-of-statement-1 lim)) + 'label)) + (if (eq step 'up) + (setq placeholder (point)) + ;; There was no containing statement afterall. + (goto-char placeholder))))) + placeholder)) + (if (looking-at c-block-stmt-2-key) + ;; Require a parenthesis after these keywords. + ;; Necessary to catch e.g. synchronized in Java, + ;; which can be used both as statement and + ;; modifier. + (and (= (c-forward-token-1 1 nil) 0) + (eq (char-after) ?\()) + (looking-at c-opt-block-stmt-key)))) + (if (eq step-type 'up) + ;; CASE 18A: Simple substatement. + (progn + (goto-char placeholder) + (cond + ((eq char-after-ip ?{) + (c-add-stmt-syntax 'substatement-open nil + containing-sexp paren-state)) + ((save-excursion + (goto-char indent-point) + (back-to-indentation) + (looking-at c-label-key)) + (c-add-stmt-syntax 'substatement-label nil + containing-sexp paren-state)) + (t + (c-add-stmt-syntax 'substatement nil + containing-sexp paren-state)))) + ;; CASE 18B: Some other substatement. This is shared + ;; with case 10. + (c-guess-continued-construct indent-point + char-after-ip + placeholder + lim + paren-state))) + ;; CASE 4: In-expression statement. C.f. cases 7B, 16A and + ;; 17E. + ((and (or c-opt-inexpr-class-key + c-opt-inexpr-block-key + c-opt-lambda-key) + (setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + containing-sexp))) (setq tmpsymbol (assq (car placeholder) '((inexpr-class . class-open) (inexpr-statement . block-open)))) @@ -1635,7 +2787,9 @@ 'lambda-intro-cont))) (goto-char (cdr placeholder)) (back-to-indentation) - (c-add-syntax tmpsymbol (point)) + (c-add-stmt-syntax tmpsymbol t + (c-most-enclosing-brace c-state-cache (point)) + (c-whack-state-after (point) paren-state)) (unless (eq (point) (cdr placeholder)) (c-add-syntax (car placeholder)))) ;; CASE 5: Line is at top level. @@ -1653,7 +2807,7 @@ (goto-char indent-point) (skip-chars-forward " \t") (and (c-safe (progn (c-backward-sexp 2) t)) - (looking-at (concat c-extra-toplevel-key "[^_]")) + (looking-at c-other-decl-block-key) (setq keyword (match-string 1) placeholder (point)) (or (and (string-equal keyword "namespace") @@ -1671,25 +2825,15 @@ ((save-excursion (goto-char indent-point) (skip-chars-forward " \t{") - ;; TBD: watch out! there could be a bogus - ;; c-state-cache in place when we get here. we have - ;; to go through much chicanery to ignore the cache. - ;; But of course, there may not be! BLECH! BOGUS! - (let ((decl - (let ((c-state-cache nil)) - (c-search-uplist-for-classkey (c-parse-state)) - ))) + (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) (and decl (setq placeholder (aref decl 0))) )) (c-add-syntax 'class-open placeholder)) ;; CASE 5A.3: brace list open ((save-excursion - (c-beginning-of-statement-1 lim) - ;; c-b-o-s could have left us at point-min - (and (bobp) - (c-forward-syntactic-ws indent-point)) - (if (looking-at "typedef[^_]") + (c-beginning-of-statement-1 lim t) + (if (looking-at "typedef\\>[^_]") (progn (c-forward-sexp 1) (c-forward-syntactic-ws indent-point))) (setq placeholder (c-point 'boi)) @@ -1704,7 +2848,7 @@ (looking-at "new\\>[^_]")) (setq tmpsymbol 'topmost-intro-cont))) (eq (char-after) ?=)) - (looking-at "enum[ \t\n]+")) + (looking-at "enum\\>[^_]")) (save-excursion (while (and (< (point) indent-point) (= (c-forward-token-1 1 t) 0) @@ -1723,22 +2867,22 @@ ;; case 10B.2. (progn (c-beginning-of-statement-1 lim) - (c-forward-syntactic-ws) (c-add-syntax 'topmost-intro-cont (c-point 'boi))) (c-add-syntax 'brace-list-open placeholder))) ;; CASE 5A.4: inline defun open ((and inclass-p (not inenclosing-p)) (c-add-syntax 'inline-open) - (c-add-class-syntax 'inclass inclass-p)) + (c-add-class-syntax 'inclass inclass-p paren-state)) ;; CASE 5A.5: ordinary defun open (t (goto-char placeholder) - (if inclass-p + (if (or inclass-p macro-start) (c-add-syntax 'defun-open (c-point 'boi)) + ;; Bogus to use bol here, but it's the legacy. (c-add-syntax 'defun-open (c-point 'bol))) ))) ;; CASE 5B: first K&R arg decl or member init - ((c-just-after-func-arglist-p) + ((c-just-after-func-arglist-p nil lim) (cond ;; CASE 5B.1: a member init ((or (eq char-before-ip ?:) @@ -1769,8 +2913,10 @@ ) ;; CASE 5B.2: K&R arg decl intro (c-recognize-knr-p + (c-beginning-of-statement-1 lim) (c-add-syntax 'knr-argdecl-intro (c-point 'boi)) - (if inclass-p (c-add-class-syntax 'inclass inclass-p))) + (if inclass-p + (c-add-class-syntax 'inclass inclass-p paren-state))) ;; CASE 5B.3: Inside a member init list. ((c-beginning-of-member-init-list lim) (c-forward-syntactic-ws) @@ -1783,12 +2929,12 @@ ))) ;; CASE 5C: inheritance line. could be first inheritance ;; line, or continuation of a multiple inheritance - ((or (and c-baseclass-key + ((or (and (c-major-mode-is 'c++-mode) (progn (when (eq char-after-ip ?,) (skip-chars-forward " \t") (forward-char)) - (looking-at c-baseclass-key))) + (looking-at c-opt-decl-spec-key))) (and (or (eq char-before-ip ?:) ;; watch out for scope operator (save-excursion @@ -1812,7 +2958,7 @@ cont done) (save-excursion (while (not done) - (cond ((looking-at c-Java-special-key) + (cond ((looking-at c-opt-decl-spec-key) (setq injava-inher (cons cont (point)) done t)) ((or (not (c-safe (c-forward-sexp -1) t)) @@ -1827,21 +2973,23 @@ (cond ;; CASE 5C.1: non-hanging colon on an inher intro ((eq char-after-ip ?:) - (c-backward-syntactic-ws lim) + (c-beginning-of-statement-1 lim) (c-add-syntax 'inher-intro (c-point 'boi)) ;; don't add inclass symbol since relative point already ;; contains any class offset ) ;; CASE 5C.2: hanging colon on an inher intro ((eq char-before-ip ?:) + (c-beginning-of-statement-1 lim) (c-add-syntax 'inher-intro (c-point 'boi)) - (if inclass-p (c-add-class-syntax 'inclass inclass-p))) + (if inclass-p + (c-add-class-syntax 'inclass inclass-p paren-state))) ;; CASE 5C.3: in a Java implements/extends (injava-inher (let ((where (cdr injava-inher)) (cont (car injava-inher))) (goto-char where) - (cond ((looking-at "throws[ \t\n]") + (cond ((looking-at "throws\\>[^_]") (c-add-syntax 'func-decl-cont (progn (c-beginning-of-statement-1 lim) (c-point 'boi)))) @@ -1858,16 +3006,21 @@ ;; don't add inclass symbol since relative point already ;; contains any class offset ))) - ;; CASE 5D: this could be a top-level compound statement, a + ;; CASE 5D: this could be a top-level initialization, a ;; member init list continuation, or a template argument ;; list continuation. ((c-with-syntax-table (if (c-major-mode-is 'c++-mode) c++-template-syntax-table (syntax-table)) (save-excursion + ;; Note: We use the fact that lim is always after any + ;; preceding brace sexp. (while (and (= (c-backward-token-1 1 t lim) 0) - (not (looking-at "[;{<,]")))) - (eq (char-after) ?,))) + (not (looking-at "[;<,=]")))) + (or (memq (char-after) '(?, ?=)) + (and (c-major-mode-is 'c++-mode) + (= (c-backward-token-1 1 nil lim) 0) + (eq (char-after) ?<))))) (goto-char indent-point) (c-beginning-of-member-init-list lim) (cond @@ -1886,9 +3039,11 @@ (parse-partial-sexp (point) placeholder))) 0) (and - (if c-access-key (not (looking-at c-access-key)) t) + (if c-opt-access-key + (not (looking-at c-opt-access-key)) t) (not (looking-at c-class-key)) - (if c-bitfield-key (not (looking-at c-bitfield-key)) t)) + (if c-opt-bitfield-key + (not (looking-at c-opt-bitfield-key)) t)) ))) (goto-char placeholder) (c-forward-syntactic-ws) @@ -1902,43 +3057,61 @@ (eq (char-after) ?:)) (skip-chars-forward " \t:") (c-add-syntax 'member-init-cont (point))) - ;; CASE 5D.3: perhaps a multiple inheritance line? - ((save-excursion - (c-beginning-of-statement-1 lim) - (setq placeholder (point)) - (looking-at c-inher-key)) + ;; CASE 5D.3: perhaps a template list continuation? + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (save-restriction + (c-with-syntax-table c++-template-syntax-table + (goto-char indent-point) + (setq placeholder (c-up-list-backward (point))) + (and placeholder + (eq (char-after placeholder) ?<)))))) + ;; we can probably indent it just like an arglist-cont + (goto-char placeholder) + (c-beginning-of-statement-1 lim t) + (c-add-syntax 'template-args-cont (c-point 'boi))) + ;; CASE 5D.4: perhaps a multiple inheritance line? + ((and (c-major-mode-is 'c++-mode) + (save-excursion + (c-beginning-of-statement-1 lim) + (setq placeholder (point)) + (if (looking-at "static\\>[^_]") + (c-forward-token-1 1 nil indent-point)) + (and (looking-at c-class-key) + (= (c-forward-token-1 2 nil indent-point) 0) + (if (eq (char-after) ?<) + (c-with-syntax-table c++-template-syntax-table + (= (c-forward-token-1 1 t indent-point) 0)) + t) + (eq (char-after) ?:)))) (goto-char placeholder) (c-add-syntax 'inher-cont (c-point 'boi))) - ;; CASE 5D.4: perhaps a template list continuation? - ((save-excursion - (goto-char indent-point) - (skip-chars-backward "^<" lim) - ;; not sure if this is the right test, but it should - ;; be fast and mostly accurate. - (setq placeholder (point)) - (and (eq (char-before) ?<) - (not (c-in-literal lim)))) - ;; we can probably indent it just like an arglist-cont - (goto-char placeholder) - (c-beginning-of-statement-1 lim) - (c-add-syntax 'template-args-cont (c-point 'boi))) - ;; CASE 5D.5: perhaps a top-level statement-cont + ;; CASE 5D.5: Continuation of the "expression part" of a + ;; top level construct. (t - (c-beginning-of-statement-1 lim) - ;; skip over any access-specifiers - (and inclass-p c-access-key - (while (looking-at c-access-key) - (forward-line 1))) - ;; skip over comments, whitespace - (c-forward-syntactic-ws indent-point) - (c-add-syntax 'statement-cont (c-point 'boi))) + (while (and (eq (car (c-beginning-of-decl-1 containing-sexp)) + 'same) + (save-excursion + (c-backward-syntactic-ws) + (eq (char-before) ?})))) + (c-add-stmt-syntax + (if (eq char-before-ip ?,) + ;; A preceding comma at the top level means that a + ;; new variable declaration starts here. Use + ;; topmost-intro-cont for it, for consistency with + ;; the first variable declaration. C.f. case 5N. + 'topmost-intro-cont + 'statement-cont) + nil containing-sexp paren-state)) )) ;; CASE 5E: we are looking at a access specifier ((and inclass-p - c-access-key - (looking-at c-access-key)) - (c-add-syntax 'access-label (c-point 'bonl)) - (c-add-class-syntax 'inclass inclass-p)) + c-opt-access-key + (looking-at c-opt-access-key)) + (setq placeholder (c-add-class-syntax 'inclass inclass-p + paren-state)) + ;; Append access-label with the same anchor point as inclass gets. + (nconc syntax (list (cons 'access-label placeholder)))) ;; CASE 5F: extern-lang-close or namespace-close? ((and inenclosing-p (eq char-after-ip ?})) @@ -1957,66 +3130,72 @@ (and (c-safe (progn (c-backward-sexp 1) t)) (= (point) (aref inclass-p 1)) )))) - (c-add-class-syntax 'class-close inclass-p)) + (c-add-class-syntax 'class-close inclass-p paren-state)) ;; CASE 5H: we could be looking at subsequent knr-argdecls ((and c-recognize-knr-p - ;; here we essentially use the hack that is used in - ;; Emacs' c-mode.el to limit how far back we should - ;; look. The assumption is made that argdecls are - ;; indented at least one space and that function - ;; headers are not indented. - (let ((limit (save-excursion - (re-search-backward "^[^ \^L\t\n#]" nil 'move) - (point)))) - (save-excursion - (c-backward-syntactic-ws limit) - (setq placeholder (point)) - (while (and (memq (char-before) '(?\; ?,)) - (> (point) limit)) - (beginning-of-line) - (setq placeholder (point)) - (c-backward-syntactic-ws limit)) - (and (eq (char-before) ?\)) - (or (not c-method-key) - (progn - (c-forward-sexp -1) - (forward-char -1) - (c-backward-syntactic-ws) - (not (or (memq (char-before) '(?- ?+)) - ;; or a class category - (progn - (c-forward-sexp -2) - (looking-at c-class-key)) - ))))) - )) + (not (eq char-before-ip ?})) (save-excursion - (c-beginning-of-statement-1) - (not (looking-at "typedef[ \t\n]+")))) + (setq placeholder (cdr (c-beginning-of-decl-1 lim))) + (and placeholder + ;; Do an extra check to avoid tripping up on + ;; statements that occur in invalid contexts + ;; (e.g. in macro bodies where we don't really + ;; know the context of what we're looking at). + (not (and c-opt-block-stmt-key + (looking-at c-opt-block-stmt-key))))) + (< placeholder indent-point)) (goto-char placeholder) - (c-add-syntax 'knr-argdecl (c-point 'boi))) + (c-add-syntax 'knr-argdecl (point))) ;; CASE 5I: ObjC method definition. - ((and c-method-key - (looking-at c-method-key)) + ((and c-opt-method-key + (looking-at c-opt-method-key)) + (c-beginning-of-statement-1 lim) (c-add-syntax 'objc-method-intro (c-point 'boi))) - ;; CASE 5J: we are at the topmost level, make sure we skip - ;; back past any access specifiers + ;; CASE 5N: At a variable declaration that follows a class + ;; definition or some other block declaration that doesn't + ;; end at the closing '}'. C.f. case 5D.5. ((progn (c-backward-syntactic-ws lim) + (and (eq (char-before) ?}) + (save-excursion + (let ((start (point))) + (if paren-state + ;; Speed up the backward search a bit. + (goto-char (car (car paren-state)))) + (c-beginning-of-decl-1 containing-sexp) + (setq placeholder (point)) + (if (= start (point)) + ;; The '}' is unbalanced. + nil + (c-end-of-decl-1) + (> (point) indent-point)))))) + (goto-char placeholder) + (c-add-stmt-syntax 'topmost-intro-cont nil + containing-sexp paren-state)) + ;; CASE 5J: we are at the topmost level, make + ;; sure we skip back past any access specifiers + ((progn (while (and inclass-p - c-access-key + c-opt-access-key (not (bobp)) (save-excursion (c-safe (progn (c-backward-sexp 1) t)) - (looking-at c-access-key))) + (looking-at c-opt-access-key))) (c-backward-sexp 1) (c-backward-syntactic-ws lim)) (or (bobp) - (memq (char-before) '(?\; ?\})))) + (memq (char-before) '(?\; ?})) + (and (c-major-mode-is 'objc-mode) + (progn + (c-beginning-of-statement-1 lim) + (eq (char-after) ?@))))) ;; real beginning-of-line could be narrowed out due to ;; enclosure in a class block (save-restriction (widen) (c-add-syntax 'topmost-intro (c-point 'bol)) + ;; Using bol instead of boi above is highly bogus, and + ;; it makes our lives hard to remain compatible. :P (if inclass-p (progn (goto-char (aref inclass-p 1)) @@ -2027,29 +3206,32 @@ (c-add-syntax 'inextern-lang (c-point 'boi))) ((eq inenclosing-p 'namespace) (c-add-syntax 'innamespace (c-point 'boi))) - (t (c-add-class-syntax 'inclass inclass-p))) + (t (c-add-class-syntax 'inclass inclass-p paren-state))) )) + (when (and c-syntactic-indentation-in-macros + macro-start + (/= macro-start (c-point 'boi indent-point))) + (c-add-syntax 'cpp-define-intro) + (setq macro-start nil)) )) - ;; CASE 5K: we are at an ObjC or Java method definition + ;; CASE 5K: we are at an ObjC method definition ;; continuation line. - ((and c-method-key + ((and c-opt-method-key (progn (c-beginning-of-statement-1 lim) (beginning-of-line) - (looking-at c-method-key))) + (looking-at c-opt-method-key))) (c-add-syntax 'objc-method-args-cont (point))) ;; CASE 5L: we are at the first argument of a template ;; arglist that begins on the previous line. ((eq (char-before) ?<) - (c-beginning-of-statement-1 lim) - (c-forward-syntactic-ws) + (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) (c-add-syntax 'template-args-cont (c-point 'boi))) ;; CASE 5M: we are at a topmost continuation line (t - (c-beginning-of-statement-1 lim) - (c-forward-syntactic-ws) + (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) (c-add-syntax 'topmost-intro-cont (c-point 'boi))) - )) ; end CASE 5 + )) ;; (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 @@ -2059,14 +3241,9 @@ (goto-char containing-sexp) (c-looking-at-special-brace-list))) (eq (char-after containing-sexp) ?{))) - (c-backward-syntactic-ws containing-sexp) (cond ;; CASE 7A: we are looking at the arglist closing paren - ((and (or (c-major-mode-is 'pike-mode) - ;; Don't check this in Pike since it allows a - ;; comma after the last arg. - (not (eq char-before-ip ?,))) - (memq char-after-ip '(?\) ?\]))) + ((memq char-after-ip '(?\) ?\])) (goto-char containing-sexp) (setq placeholder (c-point 'boi)) (when (and (c-safe (backward-up-list 1) t) @@ -2076,18 +3253,31 @@ (setq placeholder (point))) (c-add-syntax 'arglist-close placeholder)) ;; CASE 7B: Looking at the opening brace of an - ;; in-expression block or brace list. - ((eq char-after-ip ?{) - (goto-char indent-point) - (setq placeholder (c-point 'boi)) - (goto-char containing-sexp) - (if (c-inside-bracelist-p placeholder - (cons containing-sexp state)) - (progn - (c-add-syntax 'brace-list-open (c-point 'boi)) - (c-add-syntax 'inexpr-class)) - (c-add-syntax 'block-open (c-point 'boi)) - (c-add-syntax 'inexpr-statement))) + ;; 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) + c-state-cache)) + (if placeholder + (setq tmpsymbol '(brace-list-open . inexpr-class)) + (setq tmpsymbol '(block-open . inexpr-statement) + placeholder + (cdr-safe (c-looking-at-inexpr-block + (c-safe-position containing-sexp + paren-state) + containing-sexp))) + ;; placeholder is nil if it's a block directly in + ;; a function arglist. That makes us skip out of + ;; this case. + ))) + (goto-char placeholder) + (back-to-indentation) + (c-add-stmt-syntax (car tmpsymbol) t + (c-most-enclosing-brace paren-state (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) placeholder) + (c-add-syntax (cdr tmpsymbol)))) ;; CASE 7C: we are looking at the first argument in an empty ;; argument list. Use arglist-close if we're actually ;; looking at a close paren or bracket. @@ -2102,23 +3292,22 @@ (c-add-syntax 'arglist-intro placeholder)) ;; CASE 7D: we are inside a conditional test clause. treat ;; these things as statements - ((save-excursion + ((progn (goto-char containing-sexp) (and (c-safe (progn (c-forward-sexp -1) t)) (looking-at "\\<for\\>[^_]"))) (goto-char (1+ containing-sexp)) (c-forward-syntactic-ws indent-point) - (c-beginning-of-statement-1 containing-sexp) (if (eq char-before-ip ?\;) (c-add-syntax 'statement (point)) (c-add-syntax 'statement-cont (point)) )) - ;; CASE 7E: maybe a continued method call. This is the case - ;; when we are inside a [] bracketed exp, and what precede - ;; the opening bracket is not an identifier. - ((and c-method-key + ;; CASE 7E: maybe a continued ObjC method call. This is the + ;; case when we are inside a [] bracketed exp, and what + ;; precede the opening bracket is not an identifier. + ((and c-opt-method-key (eq (char-after containing-sexp) ?\[) - (save-excursion + (progn (goto-char (1- containing-sexp)) (c-backward-syntactic-ws (c-point 'bod)) (if (not (looking-at c-symbol-key)) @@ -2129,10 +3318,11 @@ ;; opening paren. This case includes multi-line ;; mathematical paren groupings, but we could be on a ;; for-list continuation line - ((save-excursion + ((progn (goto-char (1+ containing-sexp)) (skip-chars-forward " \t") - (not (eolp))) + (and (not (eolp)) + (not (looking-at "\\\\$")))) (goto-char containing-sexp) (setq placeholder (c-point 'boi)) (when (and (c-safe (backward-up-list 1) t) @@ -2143,17 +3333,15 @@ (c-add-syntax 'arglist-cont-nonempty placeholder)) ;; CASE 7G: we are looking at just a normal arglist ;; continuation line - (t (c-beginning-of-statement-1 containing-sexp) - (forward-char 1) - (c-forward-syntactic-ws indent-point) + (t (c-forward-syntactic-ws indent-point) (c-add-syntax 'arglist-cont (c-point 'boi))) )) ;; CASE 8: func-local multi-inheritance line - ((and c-baseclass-key + ((and (c-major-mode-is 'c++-mode) (save-excursion (goto-char indent-point) (skip-chars-forward " \t") - (looking-at c-baseclass-key))) + (looking-at c-opt-decl-spec-key))) (goto-char indent-point) (skip-chars-forward " \t") (cond @@ -2175,7 +3363,7 @@ (save-excursion (goto-char containing-sexp) (c-looking-at-special-brace-list))) - (c-inside-bracelist-p containing-sexp state))) + (c-inside-bracelist-p containing-sexp paren-state))) (cond ;; CASE 9A: In the middle of a special brace list opener. ((and (consp special-brace-list) @@ -2189,9 +3377,10 @@ (assoc 'statement-cont (setq placeholder (c-guess-basic-syntax)))) (setq syntax placeholder) - (c-beginning-of-statement-1 lim) + (c-beginning-of-statement-1 + (c-safe-position (1- containing-sexp) paren-state)) (c-forward-token-1 0) - (if (looking-at "typedef\\>") (c-forward-token-1 1)) + (if (looking-at "typedef\\>[^_]") (c-forward-token-1 1)) (c-add-syntax 'brace-list-open (c-point 'boi)))) ;; CASE 9B: brace-list-close brace ((if (consp special-brace-list) @@ -2211,11 +3400,15 @@ (eq (1+ (point)) (cdr (car special-brace-list))))))) ;; Normal brace list check. (and (eq char-after-ip ?}) - (c-safe (progn (forward-char 1) - (c-backward-sexp 1) + (c-safe (progn (goto-char (c-up-list-backward (point))) t)) (= (point) containing-sexp))) - (c-add-syntax 'brace-list-close (c-point 'boi))) + (if (eq (point) (c-point 'boi)) + (c-add-syntax 'brace-list-close (point)) + (setq lim (c-most-enclosing-brace c-state-cache (point))) + (c-beginning-of-statement-1 lim) + (c-add-stmt-syntax 'brace-list-close t lim + (c-whack-state-after (point) paren-state) t))) (t ;; Prepare for the rest of the cases below by going to the ;; token following the opening brace @@ -2228,13 +3421,20 @@ (let ((start (point))) (c-forward-syntactic-ws indent-point) (goto-char (max start (c-point 'bol)))) - (skip-chars-forward " \t\n\r" indent-point) + (c-skip-ws-forward indent-point) (cond ;; CASE 9C: we're looking at the first line in a brace-list ((= (point) indent-point) - (goto-char containing-sexp) - (c-add-syntax 'brace-list-intro (c-point 'boi)) - ) ; end CASE 9C + (if (consp special-brace-list) + (goto-char (car (car special-brace-list))) + (goto-char containing-sexp)) + (if (eq (point) (c-point 'boi)) + (c-add-syntax 'brace-list-intro (point)) + (setq lim (c-most-enclosing-brace c-state-cache (point))) + (c-beginning-of-statement-1 lim) + (c-add-stmt-syntax 'brace-list-intro t lim + (c-whack-state-after (point) paren-state) + t))) ;; CASE 9D: this is just a later brace-list-entry or ;; brace-entry-open (t (if (or (eq char-after-ip ?{) @@ -2245,184 +3445,63 @@ (c-looking-at-special-brace-list (point))))) (c-add-syntax 'brace-entry-open (point)) (c-add-syntax 'brace-list-entry (point)) - )) ; end CASE 9D - )))) ; end CASE 9 - ;; CASE 10: A continued statement + )) + )))) + ;; CASE 10: A continued statement or top level construct. ((and (not (memq char-before-ip '(?\; ?:))) (or (not (eq char-before-ip ?})) - (c-looking-at-inexpr-block-backward containing-sexp)) + (c-looking-at-inexpr-block-backward c-state-cache)) (> (point) (save-excursion (c-beginning-of-statement-1 containing-sexp) - (c-forward-syntactic-ws) (setq placeholder (point)))) (/= placeholder containing-sexp)) - (goto-char indent-point) - (skip-chars-forward " \t") - (let ((after-cond-placeholder - (save-excursion - (goto-char placeholder) - (if (and c-conditional-key (looking-at c-conditional-key)) - (progn - (c-safe (c-skip-conditional)) - (c-forward-syntactic-ws) - (if (eq (char-after) ?\;) - (progn - (forward-char 1) - (c-forward-syntactic-ws))) - (point)) - nil)))) - (cond - ;; CASE 10A: substatement - ((and after-cond-placeholder - (>= after-cond-placeholder indent-point)) - (goto-char placeholder) - (if (eq char-after-ip ?{) - (c-add-syntax 'substatement-open (c-point 'boi)) - (c-add-syntax 'substatement (c-point 'boi)))) - ;; CASE 10B: open braces for class or brace-lists - ((setq special-brace-list - (or (and c-special-brace-lists - (c-looking-at-special-brace-list)) - (eq char-after-ip ?{))) - (cond - ;; CASE 10B.1: class-open - ((save-excursion - (goto-char indent-point) - (skip-chars-forward " \t{") - (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) - (and decl - (setq placeholder (aref decl 0))) - )) - (c-add-syntax 'class-open placeholder)) - ;; CASE 10B.2: brace-list-open - ((or (consp special-brace-list) - (save-excursion - (goto-char placeholder) - (looking-at "\\<enum\\>")) - (save-excursion - (goto-char indent-point) - (while (and (> (point) placeholder) - (= (c-backward-token-1 1 t) 0) - (/= (char-after) ?=))) - (eq (char-after) ?=))) - ;; The most semantically accurate symbol here is - ;; brace-list-open, but we report it simply as a - ;; statement-cont. The reason is that one normally - ;; adjusts brace-list-open for brace lists as - ;; top-level constructs, and brace lists inside - ;; statements is a completely different context. - (goto-char indent-point) - (c-beginning-of-closest-statement) - (c-add-syntax 'statement-cont (c-point 'boi))) - ;; CASE 10B.3: The body of a function declared inside a - ;; normal block. This can only occur in Pike. - ((and (c-major-mode-is 'pike-mode) - (progn - (goto-char indent-point) - (not (c-looking-at-bos)))) - (c-beginning-of-closest-statement) - (c-add-syntax 'defun-open (c-point 'boi))) - ;; CASE 10B.4: catch-all for unknown construct. - (t - ;; Can and should I add an extensibility hook here? - ;; Something like c-recognize-hook so support for - ;; unknown constructs could be added. It's probably a - ;; losing proposition, so I dunno. - (goto-char placeholder) - (c-add-syntax 'statement-cont (c-point 'boi)) - (c-add-syntax 'block-open)) - )) - ;; CASE 10C: iostream insertion or extraction operator - ((looking-at "<<\\|>>") - (goto-char placeholder) - (and after-cond-placeholder - (goto-char after-cond-placeholder)) - (while (and (re-search-forward "<<\\|>>" indent-point 'move) - (c-in-literal placeholder))) - ;; if we ended up at indent-point, then the first - ;; streamop is on a separate line. Indent the line like - ;; a statement-cont instead - (if (/= (point) indent-point) - (c-add-syntax 'stream-op (c-point 'boi)) - (c-backward-syntactic-ws lim) - (c-add-syntax 'statement-cont (c-point 'boi)))) - ;; CASE 10D: continued statement. find the accurate - ;; beginning of statement or substatement - (t - (c-beginning-of-statement-1 after-cond-placeholder) - ;; KLUDGE ALERT! c-beginning-of-statement-1 can leave - ;; us before the lim we're passing in. It should be - ;; fixed, but I'm worried about side-effects at this - ;; late date. Fix for v5. - (goto-char (or (and after-cond-placeholder - (max after-cond-placeholder (point))) - (point))) - (c-add-syntax 'statement-cont (point))) - ))) - ;; CASE 11: an else clause? - ((looking-at "\\<else\\>[^_]") - (c-backward-to-start-of-if containing-sexp) - (c-add-syntax 'else-clause (c-point 'boi))) - ;; CASE 12: Statement. But what kind? Lets see if its a - ;; while closure of a do/while construct - ((progn - (goto-char indent-point) - (skip-chars-forward " \t") - (and (looking-at "while\\b[^_]") - (save-excursion - (c-backward-to-start-of-do containing-sexp) - (setq placeholder (point)) - (looking-at "do\\b[^_]")) - )) - (goto-char placeholder) - (c-add-syntax 'do-while-closure (c-point 'boi))) - ;; CASE 13: A catch or finally clause? This case is simpler - ;; than if-else and do-while, because a block is required - ;; after every try, catch and finally. - ((save-excursion - (and (cond ((c-major-mode-is 'c++-mode) - (looking-at "\\<catch\\>[^_]")) - ((c-major-mode-is 'java-mode) - (looking-at "\\<\\(catch\\|finally\\)\\>[^_]"))) - (c-safe (c-backward-sexp) t) - (eq (char-after) ?{) - (c-safe (c-backward-sexp) t) - (if (eq (char-after) ?\() - (c-safe (c-backward-sexp) t) - t) - (looking-at "\\<\\(try\\|catch\\)\\>[^_]") - (setq placeholder (c-point 'boi)))) - (c-add-syntax 'catch-clause placeholder)) + ;; This is shared with case 18. + (c-guess-continued-construct indent-point + char-after-ip + placeholder + containing-sexp + paren-state)) ;; CASE 14: A case or default label - ((looking-at c-switch-label-key) + ((looking-at c-label-kwds-regexp) (goto-char containing-sexp) - ;; check for hanging braces - (if (/= (point) (c-point 'boi)) - (c-forward-sexp -1)) - (c-add-syntax 'case-label (c-point 'boi))) + (setq lim (c-most-enclosing-brace c-state-cache containing-sexp)) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'case-label t lim paren-state)) ;; CASE 15: any other label ((looking-at c-label-key) (goto-char containing-sexp) - ;; check for hanging braces - (if (/= (point) (c-point 'boi)) - (c-forward-sexp -1)) - (c-add-syntax 'label (c-point 'boi))) + (setq lim (c-most-enclosing-brace c-state-cache containing-sexp)) + (save-excursion + (setq tmpsymbol + (if (and (eq (c-beginning-of-statement-1 lim) 'up) + (looking-at "switch\\>[^_]")) + ;; If the surrounding statement is a switch then + ;; let's analyze all labels as switch labels, so + ;; that they get lined up consistently. + 'case-label + 'label))) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax tmpsymbol t lim paren-state)) ;; CASE 16: block close brace, possibly closing the defun or ;; the class ((eq char-after-ip ?}) - (let* ((lim (c-safe-position containing-sexp fullstate)) - (relpos (save-excursion - (goto-char containing-sexp) - (if (/= (point) (c-point 'boi)) - (c-beginning-of-statement-1 lim)) - (c-point 'boi)))) + ;; From here on we have the next containing sexp in lim. + (setq lim (c-most-enclosing-brace paren-state)) + (goto-char containing-sexp) (cond + ;; CASE 16E: Closing a statement block? This catches + ;; cases where it's preceded by a statement keyword, + ;; which works even when used in an "invalid" context, + ;; e.g. a macro argument. + ((c-after-conditional) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'block-close t lim paren-state)) ;; CASE 16A: closing a lambda defun or an in-expression - ;; block? - ((save-excursion - (goto-char containing-sexp) - (setq placeholder (c-looking-at-inexpr-block))) + ;; block? C.f. cases 4, 7B and 17E. + ((setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + nil)) (setq tmpsymbol (if (eq (car placeholder) 'inlambda) 'inline-close 'block-close)) @@ -2432,32 +3511,56 @@ (c-add-syntax tmpsymbol (point)) (goto-char (cdr placeholder)) (back-to-indentation) - (c-add-syntax tmpsymbol (point)) + (c-add-stmt-syntax tmpsymbol t + (c-most-enclosing-brace paren-state (point)) + (c-whack-state-after (point) paren-state)) (if (/= (point) (cdr placeholder)) (c-add-syntax (car placeholder))))) ;; CASE 16B: does this close an inline or a function in ;; an extern block or namespace? - ((progn - (goto-char containing-sexp) - (setq placeholder (c-search-uplist-for-classkey state))) - (goto-char (aref placeholder 0)) - (if (looking-at (concat c-extra-toplevel-key "[^_]")) - (c-add-syntax 'defun-close relpos) - (c-add-syntax 'inline-close relpos))) + ((setq placeholder (c-search-uplist-for-classkey paren-state)) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (if (save-excursion + (goto-char (aref placeholder 0)) + (looking-at c-other-decl-block-key)) + (c-add-syntax 'defun-close (point)) + (c-add-syntax 'inline-close (point)))) + ;; CASE 16F: Can be a defun-close of a function declared + ;; in a statement block, e.g. in Pike or when using gcc + ;; extensions. Might also trigger it with some macros + ;; followed by blocks, and this gives sane indentation + ;; then too. Let it through to be handled below. + ;; C.f. cases B.3 and 17G. + ((and (not inenclosing-p) + lim + (save-excursion + (and (not (c-looking-at-bos)) + (eq (c-beginning-of-statement-1 lim nil nil t) 'same) + (setq placeholder (point))))) + (back-to-indentation) + (if (/= (point) containing-sexp) + (goto-char placeholder)) + (c-add-stmt-syntax 'defun-close t lim paren-state)) ;; CASE 16C: if there an enclosing brace that hasn't ;; been narrowed out by a class, then this is a - ;; block-close - ((and (not inenclosing-p) - (c-most-enclosing-brace state) - (or (not (c-major-mode-is 'pike-mode)) - ;; In Pike it can be a defun-close of a - ;; function declared in a statement block. Let - ;; it through to be handled below. - (or (c-looking-at-bos) - (progn - (c-beginning-of-statement-1) - (looking-at c-conditional-key))))) - (c-add-syntax 'block-close relpos)) + ;; block-close. C.f. case 17H. + ((and (not inenclosing-p) lim) + ;; If the block is preceded by a case/switch label on + ;; the same line, we anchor at the first preceding label + ;; at boi. The default handling in c-add-stmt-syntax is + ;; really fixes it better, but we do like this to keep + ;; the indentation compatible with version 5.28 and + ;; earlier. + (while (and (/= (setq placeholder (point)) (c-point 'boi)) + (eq (c-beginning-of-statement-1 lim) 'label))) + (goto-char placeholder) + (if (looking-at c-label-kwds-regexp) + (c-add-syntax 'block-close (point)) + (goto-char containing-sexp) + ;; c-backward-to-block-anchor not necessary here; those + ;; situations are handled in case 16E above. + (c-add-stmt-syntax 'block-close t lim paren-state))) ;; CASE 16D: find out whether we're closing a top-level ;; class or a defun (t @@ -2465,204 +3568,182 @@ (narrow-to-region (point-min) indent-point) (let ((decl (c-search-uplist-for-classkey (c-parse-state)))) (if decl - (c-add-class-syntax 'class-close decl) - (c-add-syntax 'defun-close relpos))))) - ))) - ;; CASE 17: statement catchall + (c-add-class-syntax 'class-close decl paren-state) + (goto-char containing-sexp) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (c-add-syntax 'defun-close (point))))) + ))) + ;; CASE 17: Statement or defun catchall. (t - ;; we know its a statement, but we need to find out if it is - ;; the first statement in a block - (goto-char containing-sexp) - (forward-char 1) - (c-forward-syntactic-ws indent-point) - ;; now skip forward past any case/default clauses we might find. - (while (or (c-skip-case-statement-forward fullstate indent-point) - (and (looking-at c-switch-label-key) - (not inswitch-p))) - (setq inswitch-p t)) - ;; we want to ignore non-case labels when skipping forward - (while (and (looking-at c-label-key) - (goto-char (match-end 0))) - (c-forward-syntactic-ws indent-point)) + (goto-char indent-point) + ;; Back up statements until we find one that starts at boi. + (while (let* ((prev-point (point)) + (last-step-type (c-beginning-of-statement-1 + containing-sexp))) + (if (= (point) prev-point) + (progn + (setq step-type (or step-type last-step-type)) + nil) + (setq step-type last-step-type) + (/= (point) (c-point 'boi))))) (cond - ;; CASE 17A: we are inside a case/default clause inside a - ;; switch statement. find out if we are at the statement - ;; just after the case/default label. - ((and inswitch-p - (progn - (goto-char indent-point) - (c-beginning-of-statement-1 containing-sexp) - (setq placeholder (point)) - (beginning-of-line) - (when (re-search-forward c-switch-label-key - (max placeholder (c-point 'eol)) t) - (setq placeholder (match-beginning 0))))) - (goto-char indent-point) - (skip-chars-forward " \t") - (if (eq (char-after) ?{) - (c-add-syntax 'statement-case-open placeholder) - (c-add-syntax 'statement-case-intro placeholder))) ;; CASE 17B: continued statement - ((eq char-before-ip ?,) - (goto-char indent-point) - (c-beginning-of-closest-statement) - (c-add-syntax 'statement-cont (c-point 'boi))) - ;; CASE 17C: a question/colon construct? But make sure - ;; what came before was not a label, and what comes after - ;; is not a globally scoped function call! - ((or (and (memq char-before-ip '(?: ??)) - (save-excursion - (goto-char indent-point) - (c-backward-syntactic-ws lim) - (back-to-indentation) - (not (looking-at c-label-key)))) - (and (memq char-after-ip '(?: ??)) - (save-excursion - (goto-char indent-point) - (skip-chars-forward " \t") - ;; watch out for scope operator - (not (looking-at "::"))))) - (goto-char indent-point) - (c-beginning-of-closest-statement) - (c-add-syntax 'statement-cont (c-point 'boi))) + ((and (eq step-type 'same) + (/= (point) indent-point)) + (c-add-stmt-syntax 'statement-cont nil + containing-sexp paren-state)) + ;; CASE 17A: After a case/default label? + ((progn + (while (and (eq step-type 'label) + (not (looking-at c-label-kwds-regexp))) + (setq step-type + (c-beginning-of-statement-1 containing-sexp))) + (eq step-type 'label)) + (c-add-stmt-syntax (if (eq char-after-ip ?{) + 'statement-case-open + 'statement-case-intro) + t containing-sexp paren-state)) ;; CASE 17D: any old statement - ((< (point) indent-point) - (let ((safepos (c-most-enclosing-brace fullstate)) - relpos done) - (goto-char indent-point) - (c-beginning-of-statement-1 safepos) - ;; It is possible we're on the brace that opens a nested - ;; function. - (if (and (eq (char-after) ?{) - (save-excursion - (c-backward-syntactic-ws safepos) - (not (eq (char-before) ?\;)))) - (c-beginning-of-statement-1 safepos)) - (if (and inswitch-p - (looking-at c-switch-label-key)) - (progn - (goto-char (match-end 0)) - (c-forward-syntactic-ws))) - (setq relpos (c-point 'boi)) - (while (and (not done) - (<= safepos (point)) - (/= relpos (point))) - (c-beginning-of-statement-1 safepos) - (if (= relpos (c-point 'boi)) - (setq done t)) - (setq relpos (c-point 'boi))) - (c-add-syntax 'statement relpos) - (if (eq char-after-ip ?{) - (c-add-syntax 'block-open)))) - ;; CASE 17E: first statement in an in-expression block - ((setq placeholder - (save-excursion - (goto-char containing-sexp) - (c-looking-at-inexpr-block))) - (goto-char containing-sexp) + ((progn + (while (eq step-type 'label) + (setq step-type + (c-beginning-of-statement-1 containing-sexp))) + (eq step-type 'previous)) + (c-add-stmt-syntax 'statement t containing-sexp paren-state) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17I: Inside a substatement block. + ((progn + ;; The following tests are all based on containing-sexp. + (goto-char containing-sexp) + ;; From here on we have the next containing sexp in lim. + (setq lim (c-most-enclosing-brace paren-state containing-sexp)) + (c-after-conditional)) + (c-backward-to-block-anchor lim) + (c-add-stmt-syntax 'statement-block-intro t lim paren-state) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) + ;; CASE 17E: first statement in an in-expression block. + ;; C.f. cases 4, 7B and 16A. + ((setq placeholder (c-looking-at-inexpr-block + (c-safe-position containing-sexp paren-state) + nil)) + (setq tmpsymbol (if (eq (car placeholder) 'inlambda) + 'defun-block-intro + 'statement-block-intro)) (back-to-indentation) - (let ((block-intro (if (eq (car placeholder) 'inlambda) - 'defun-block-intro - 'statement-block-intro))) - (if (= containing-sexp (point)) - (c-add-syntax block-intro (point)) - (goto-char (cdr placeholder)) - (back-to-indentation) - (c-add-syntax block-intro (point)) - (if (/= (point) (cdr placeholder)) - (c-add-syntax (car placeholder))))) + (if (= containing-sexp (point)) + (c-add-syntax tmpsymbol (point)) + (goto-char (cdr placeholder)) + (back-to-indentation) + (c-add-stmt-syntax tmpsymbol t + (c-most-enclosing-brace c-state-cache (point)) + (c-whack-state-after (point) paren-state)) + (if (/= (point) (cdr placeholder)) + (c-add-syntax (car placeholder)))) (if (eq char-after-ip ?{) (c-add-syntax 'block-open))) ;; CASE 17F: first statement in an inline, or first ;; statement in a top-level defun. we can tell this is it ;; if there are no enclosing braces that haven't been - ;; narrowed out by a class (i.e. don't use bod here!) + ;; narrowed out by a class (i.e. don't use bod here). + ;; However, we first check for statements that we can + ;; recognize by keywords. That increases the robustness in + ;; cases where statements are used on the top level, + ;; e.g. in macro definitions. ((save-excursion (save-restriction (widen) - (goto-char containing-sexp) - (c-narrow-out-enclosing-class state containing-sexp) - (not (c-most-enclosing-brace state)))) - (goto-char containing-sexp) - ;; if not at boi, then defun-opening braces are hung on - ;; right side, so we need a different relpos - (if (/= (point) (c-point 'boi)) - (progn - (c-backward-syntactic-ws) - (c-safe (c-forward-sexp (if (eq (char-before) ?\)) - -1 -2))) - ;; looking at a Java throws clause following a - ;; method's parameter list - (c-beginning-of-statement-1) - )) - (c-add-syntax 'defun-block-intro (c-point 'boi))) + (c-narrow-out-enclosing-class paren-state containing-sexp) + (not (c-most-enclosing-brace paren-state)))) + (c-backward-to-decl-anchor lim) + (back-to-indentation) + (c-add-syntax 'defun-block-intro (point))) ;; CASE 17G: First statement in a function declared inside - ;; a normal block. This can only occur in Pike. - ((and (c-major-mode-is 'pike-mode) - (progn - (goto-char containing-sexp) - (and (not (c-looking-at-bos)) - (progn - (c-beginning-of-statement-1) - (not (looking-at c-conditional-key)))))) - (c-add-syntax 'defun-block-intro (c-point 'boi))) - ;; CASE 17H: first statement in a block - (t (goto-char containing-sexp) - (if (/= (point) (c-point 'boi)) - (c-beginning-of-statement-1 - (if (= (point) lim) - (c-safe-position (point) state) lim))) - (c-add-syntax 'statement-block-intro (c-point 'boi)) - (if (eq char-after-ip ?{) - (c-add-syntax 'block-open))) + ;; a normal block. This can occur in Pike and with + ;; e.g. the gcc extensions. Might also trigger it with + ;; some macros followed by blocks, and this gives sane + ;; indentation then too. C.f. cases B.3 and 16F. + ((save-excursion + (and (not (c-looking-at-bos)) + (eq (c-beginning-of-statement-1 lim nil nil t) 'same) + (setq placeholder (point)))) + (back-to-indentation) + (if (/= (point) containing-sexp) + (goto-char placeholder)) + (c-add-stmt-syntax 'defun-block-intro t lim paren-state)) + ;; CASE 17H: First statement in a block. C.f. case 16C. + (t + ;; If the block is preceded by a case/switch label on the + ;; same line, we anchor at the first preceding label at + ;; boi. The default handling in c-add-stmt-syntax is + ;; really fixes it better, but we do like this to keep the + ;; indentation compatible with version 5.28 and earlier. + (while (and (/= (setq placeholder (point)) (c-point 'boi)) + (eq (c-beginning-of-statement-1 lim) 'label))) + (goto-char placeholder) + (if (looking-at c-label-kwds-regexp) + (c-add-syntax 'statement-block-intro (point)) + (goto-char containing-sexp) + ;; c-backward-to-block-anchor not necessary here; those + ;; situations are handled in case 17I above. + (c-add-stmt-syntax 'statement-block-intro t lim paren-state)) + (if (eq char-after-ip ?{) + (c-add-syntax 'block-open))) )) ) ;; now we need to look at any modifiers (goto-char indent-point) (skip-chars-forward " \t") - (cond - ;; are we looking at a comment only line? - ((and (looking-at c-comment-start-regexp) - (/= (c-forward-token-1 0 nil (c-point 'eol)) 0)) + ;; are we looking at a comment only line? + (when (and (looking-at c-comment-start-regexp) + (/= (c-forward-token-1 0 nil (c-point 'eol)) 0)) (c-add-syntax 'comment-intro)) - ;; we might want to give additional offset to friends (in C++). - ((and (c-major-mode-is 'c++-mode) - (looking-at c-C++-friend-key)) + ;; we might want to give additional offset to friends (in C++). + (when (and c-opt-friend-key + (looking-at c-opt-friend-key)) (c-add-syntax 'friend)) - ;; Start of a preprocessor directive? - ((and (eq literal 'pound) - (= (save-excursion - (c-beginning-of-macro lim) - (setq placeholder (point))) - (c-point 'boi)) - (not (and (c-major-mode-is 'pike-mode) - (eq (char-after (1+ placeholder)) ?\")))) - (c-add-syntax 'cpp-macro))) + ;; Start of or a continuation of a preprocessor directive? + (if (and macro-start + (eq macro-start (c-point 'boi)) + (not (and (c-major-mode-is 'pike-mode) + (eq (char-after (1+ macro-start)) ?\")))) + (c-add-syntax 'cpp-macro) + (when (and c-syntactic-indentation-in-macros macro-start) + (if in-macro-expr + (when (or (< syntactic-relpos macro-start) + (not (or (assq 'arglist-intro syntax) + (assq 'arglist-cont syntax) + (assq 'arglist-cont-nonempty syntax) + (assq 'arglist-close syntax)))) + ;; If inside a cpp expression, i.e. anywhere in a + ;; cpp directive except a #define body, we only let + ;; through the syntactic analysis that is internal + ;; in the expression. That means the arglist + ;; elements, if they are anchored inside the cpp + ;; expression. + (setq syntax `((cpp-macro-cont . ,macro-start)))) + (when (and (eq macro-start syntactic-relpos) + (not (assq 'cpp-define-intro syntax)) + (save-excursion + (goto-char macro-start) + (or (not (c-forward-to-cpp-define-body)) + (<= (point) (c-point 'boi indent-point))))) + ;; Inside a #define body and the syntactic analysis is + ;; anchored on the start of the #define. In this case + ;; we add cpp-define-intro to get the extra + ;; indentation of the #define body. + (c-add-syntax 'cpp-define-intro))))) ;; return the syntax syntax)))) (defun c-echo-parsing-error (&optional quiet) - (when (and c-parsing-error (not quiet)) - (message "%s" c-parsing-error) - (ding)) + (when (and c-report-syntactic-errors c-parsing-error (not quiet)) + (c-benign-error "%s" c-parsing-error)) c-parsing-error) -(defun c-shift-line-indentation (shift-amt) - (let ((pos (- (point-max) (point))) - (col (current-indentation))) - (if (zerop shift-amt) - nil - (delete-region (c-point 'bol) (c-point 'boi)) - (beginning-of-line) - (indent-to (+ col shift-amt))) - (if (< (point) (c-point 'boi)) - (back-to-indentation) - ;; If initial point was within line's indentation, position after - ;; the indentation. Else stay at same point in text. - (if (> (- (point-max) pos) (point)) - (goto-char (- (point-max) pos)))))) - (defun c-evaluate-offset (offset langelem symbol) ;; offset can be a number, a function, a variable, a list, or one of ;; the symbols + or - @@ -2683,113 +3764,59 @@ (while (and (not done) offset) (setq done (c-evaluate-offset (car offset) langelem symbol) offset (cdr offset))) - (if (not done) - (if c-strict-syntax-p - (error "No offset found for syntactic symbol %s" symbol)) - done))) + (if (and c-strict-syntax-p (not done)) + (c-benign-error "No offset found for syntactic symbol %s" symbol)) + done)) (t (symbol-value offset)) )) (defun c-get-offset (langelem) - ;; Get offset from LANGELEM which is a cons cell of the form: - ;; (SYMBOL . RELPOS). The symbol is matched against - ;; c-offsets-alist and the offset found there is either returned, - ;; or added to the indentation at RELPOS. If RELPOS is nil, then - ;; the offset is simply returned. + "Get offset from LANGELEM which is a cons cell of the form: +\(SYMBOL . RELPOS). The symbol is matched against `c-offsets-alist' +and the offset found there is returned." (let* ((symbol (car langelem)) - (relpos (cdr langelem)) (match (assq symbol c-offsets-alist)) (offset (cdr-safe match))) - (if (not match) - (if c-strict-syntax-p - (error "No offset found for syntactic symbol %s" symbol) - (setq offset 0 - relpos 0)) - (setq offset (c-evaluate-offset offset langelem symbol))) + (if match + (setq offset (c-evaluate-offset offset langelem symbol)) + (if c-strict-syntax-p + (c-benign-error "No offset found for syntactic symbol %s" symbol)) + (setq offset 0)) (if (vectorp offset) offset - (+ (if (and relpos - (< relpos (c-point 'bol))) - (save-excursion - (goto-char relpos) - (current-column)) - 0) - (or (and (numberp offset) offset) - (and (symbolp offset) (symbol-value offset)) - 0))) + (or (and (numberp offset) offset) + (and (symbolp offset) (symbol-value offset)) + 0)) )) (defun c-get-syntactic-indentation (langelems) - ;; Apply c-get-offset to a list of langelem cells to get the total - ;; syntactic indentation. Special treatment is needed for vectors - ;; containing absolute columns. - (let ((indent 0)) + "Apply `c-get-offset' to a list of langelem cells to get the total +syntactic indentation. The anchor position, whose column is used as a +base for all the collected offsets, is taken from the first element +with a relpos." + ;; Note that topmost-intro always has a relpos at bol, for + ;; historical reasons. It's often used together with other symbols + ;; that has more sane positions. Since we always use the first + ;; found relpos, we rely on that these other symbols always precede + ;; topmost-intro in the LANGELEMS list. + (let ((indent 0) anchor) (catch 'done (while langelems (let ((res (c-get-offset (car langelems)))) (if (vectorp res) (throw 'done (elt res 0)) + (unless anchor + (let ((relpos (cdr (car langelems)))) + (if relpos + (setq anchor relpos)))) (setq indent (+ indent res) langelems (cdr langelems))))) - indent))) - -(defun c-indent-line (&optional syntax quiet) - ;; Indent the current line according to the syntactic context, if - ;; c-syntactic-indentation is non-nil. Optional SYNTAX is the - ;; syntactic information for the current line. Be silent about - ;; syntactic errors if the optional argument QUIET is non-nil. - ;; Returns the amount of indentation change (in columns). - (let (shift-amt) - (if c-syntactic-indentation - (setq c-parsing-error - (or (let* ((c-parsing-error nil) - (c-syntactic-context (or syntax - c-syntactic-context - (c-guess-basic-syntax))) - (indent (c-get-syntactic-indentation c-syntactic-context))) - (and (not (c-echo-parsing-error quiet)) - c-echo-syntactic-information-p - (message "syntax: %s, indent: %d" - c-syntactic-context indent)) - (setq shift-amt (- indent (current-indentation))) - (c-shift-line-indentation shift-amt) - (run-hooks 'c-special-indent-hook) - c-parsing-error) - c-parsing-error)) - (let ((indent 0)) - (save-excursion - (while (and (= (forward-line -1) 0) - (if (looking-at "\\s-*$") - t - (back-to-indentation) - (setq indent (current-indentation)) - nil)))) - (setq shift-amt (- indent (current-indentation))) - (c-shift-line-indentation shift-amt))) - shift-amt)) - -(defun c-show-syntactic-information (arg) - "Show syntactic information for current line. -With universal argument, inserts the analysis as a comment on that line." - (interactive "P") - (let ((syntax (c-guess-basic-syntax))) - (if (not (consp arg)) - (message "syntactic analysis: %s" syntax) - (indent-for-comment) - (insert (format "%s" syntax)) - )) - (c-keep-region-active)) - -(defun c-syntactic-information-on-region (from to) - "Inserts a comment with the syntactic analysis on every line in the region." - (interactive "*r") - (save-excursion - (save-restriction - (narrow-to-region from to) - (goto-char (point-min)) - (while (not (eobp)) - (c-show-syntactic-information '(0)) - (forward-line))))) + (+ indent + (if anchor + (save-excursion + (goto-char anchor) + (current-column)) + 0))))) (cc-provide 'cc-engine)