# HG changeset patch # User Alan Mackenzie # Date 1260455351 0 # Node ID 0a0832d4518d88c69dd35bbd2c2d3bf21ccf95a4 # Parent 3ecd902dead9be1826d81f400cbec7b01718825b Fix bug#5091: indentation in c++-mode. * cc-mode.el (c-basic-common-init): make text property `category' rear non-sticky. * cc-engine.el (c-ssb-lit-begin): New defsubst, extracted from .... (c-syntactic-skip-backward): Refactor, extracting the above. (c-guess-basic-syntax CASEs 5D.3, 5L): Add extra anchor point; (c-guess-basic-syntax CASE 19): New CASE to handle template construct continued over line boundary. (c-guess-basic-syntax CASE 7): don't trigger on '<'. diff -r 3ecd902dead9 -r 0a0832d4518d lisp/progmodes/cc-engine.el --- a/lisp/progmodes/cc-engine.el Thu Dec 10 14:24:42 2009 +0000 +++ b/lisp/progmodes/cc-engine.el Thu Dec 10 14:29:11 2009 +0000 @@ -3743,6 +3743,57 @@ (goto-char bound)) nil))) +(defsubst c-ssb-lit-begin () + ;; Return the start of the literal point is in, or nil. + ;; We read and write the variables `safe-pos', `safe-pos-list', `state' + ;; bound in the caller. + + ;; Use `parse-partial-sexp' from a safe position down to the point to check + ;; if it's outside comments and strings. + (save-excursion + (let ((pos (point)) safe-pos state pps-end-pos) + ;; Pick a safe position as close to the point as possible. + ;; + ;; FIXME: Consult `syntax-ppss' here if our cache doesn't give a good + ;; position. + + (while (and safe-pos-list + (> (car safe-pos-list) (point))) + (setq safe-pos-list (cdr safe-pos-list))) + (unless (setq safe-pos (car-safe safe-pos-list)) + (setq safe-pos (max (or (c-safe-position + (point) (or c-state-cache + (c-parse-state))) + 0) + (point-min)) + safe-pos-list (list safe-pos))) + + ;; Cache positions along the way to use if we have to back up more. We + ;; cache every closing paren on the same level. If the paren cache is + ;; relevant in this region then we're typically already on the same + ;; level as the target position. Note that we might cache positions + ;; after opening parens in case safe-pos is in a nested list. That's + ;; both uncommon and harmless. + (while (progn + (setq state (parse-partial-sexp + safe-pos pos 0)) + (< (point) pos)) + (setq safe-pos (point) + safe-pos-list (cons safe-pos safe-pos-list))) + + ;; If the state contains the start of the containing sexp we cache that + ;; position too, so that parse-partial-sexp in the next run has a bigger + ;; chance of starting at the same level as the target position and thus + ;; will get more good safe positions into the list. + (if (elt state 1) + (setq safe-pos (1+ (elt state 1)) + safe-pos-list (cons safe-pos safe-pos-list))) + + (if (or (elt state 3) (elt state 4)) + ;; Inside string or comment. Continue search at the + ;; beginning of it. + (elt state 8))))) + (defun c-syntactic-skip-backward (skip-chars &optional limit paren-level) "Like `skip-chars-backward' but only look at syntactically relevant chars, i.e. don't stop at positions inside syntactic whitespace or string @@ -3761,140 +3812,100 @@ comment at the start of cc-engine.el for more info." (let ((start (point)) - state + state-2 ;; A list of syntactically relevant positions in descending ;; order. It's used to avoid scanning repeatedly over ;; potentially large regions with `parse-partial-sexp' to verify - ;; each position. + ;; each position. Used in `c-ssb-lit-begin' safe-pos-list - ;; The position at the beginning of `safe-pos-list'. - safe-pos ;; The result from `c-beginning-of-macro' at the start position or the ;; start position itself if it isn't within a macro. Evaluated on ;; demand. start-macro-beg ;; The earliest position after the current one with the same paren ;; level. Used only when `paren-level' is set. + lit-beg (paren-level-pos (point))) - (while (progn - (while (and - (< (skip-chars-backward skip-chars limit) 0) - - ;; Use `parse-partial-sexp' from a safe position down to - ;; the point to check if it's outside comments and - ;; strings. - (let ((pos (point)) state-2 pps-end-pos) - ;; Pick a safe position as close to the point as - ;; possible. - ;; - ;; FIXME: Consult `syntax-ppss' here if our - ;; cache doesn't give a good position. - (while (and safe-pos-list - (> (car safe-pos-list) (point))) - (setq safe-pos-list (cdr safe-pos-list))) - (unless (setq safe-pos (car-safe safe-pos-list)) - (setq safe-pos (max (or (c-safe-position - (point) (or c-state-cache - (c-parse-state))) - 0) - (point-min)) - safe-pos-list (list safe-pos))) - - ;; Cache positions along the way to use if we have to - ;; back up more. We cache every closing paren on the - ;; same level. If the paren cache is relevant in this - ;; region then we're typically already on the same - ;; level as the target position. Note that we might - ;; cache positions after opening parens in case - ;; safe-pos is in a nested list. That's both uncommon - ;; and harmless. - (while (progn - (setq state (parse-partial-sexp - safe-pos pos 0)) - (< (point) pos)) - (setq safe-pos (point) - safe-pos-list (cons safe-pos safe-pos-list))) - - (cond - ((or (elt state 3) (elt state 4)) - ;; Inside string or comment. Continue search at the - ;; beginning of it. - (goto-char (elt state 8)) - t) - - ((and paren-level - (save-excursion - (setq state-2 (parse-partial-sexp - pos paren-level-pos -1) - pps-end-pos (point)) - (/= (car state-2) 0))) - ;; Not at the right level. - - (if (and (< (car state-2) 0) - ;; We stop above if we go out of a paren. - ;; Now check whether it precedes or is - ;; nested in the starting sexp. - (save-excursion - (setq state-2 - (parse-partial-sexp - pps-end-pos paren-level-pos - nil nil state-2)) - (< (car state-2) 0))) - - ;; We've stopped short of the starting position - ;; so the hit was inside a nested list. Go up - ;; until we are at the right level. - (condition-case nil + (while + (progn + ;; The next loop "tries" to find the end point each time round, + ;; loops when it hasn't succeeded. + (while + (and + (< (skip-chars-backward skip-chars limit) 0) + + (let ((pos (point)) state-2 pps-end-pos) + + (cond + ;; Don't stop inside a literal + ((setq lit-beg (c-ssb-lit-begin)) + (goto-char lit-beg) + t) + + ((and paren-level + (save-excursion + (setq state-2 (parse-partial-sexp + pos paren-level-pos -1) + pps-end-pos (point)) + (/= (car state-2) 0))) + ;; Not at the right level. + + (if (and (< (car state-2) 0) + ;; We stop above if we go out of a paren. + ;; Now check whether it precedes or is + ;; nested in the starting sexp. + (save-excursion + (setq state-2 + (parse-partial-sexp + pps-end-pos paren-level-pos + nil nil state-2)) + (< (car state-2) 0))) + + ;; We've stopped short of the starting position + ;; so the hit was inside a nested list. Go up + ;; until we are at the right level. + (condition-case nil + (progn + (goto-char (scan-lists pos -1 + (- (car state-2)))) + (setq paren-level-pos (point)) + (if (and limit (>= limit paren-level-pos)) (progn - (goto-char (scan-lists pos -1 - (- (car state-2)))) - (setq paren-level-pos (point)) - (if (and limit (>= limit paren-level-pos)) - (progn - (goto-char limit) - nil) - t)) - (error - (goto-char (or limit (point-min))) - nil)) - - ;; The hit was outside the list at the start - ;; position. Go to the start of the list and exit. - (goto-char (1+ (elt state-2 1))) - nil)) - - ((c-beginning-of-macro limit) - ;; Inside a macro. - (if (< (point) - (or start-macro-beg - (setq start-macro-beg - (save-excursion - (goto-char start) - (c-beginning-of-macro limit) - (point))))) - t - - ;; It's inside the same macro we started in so it's - ;; a relevant match. - (goto-char pos) - nil))))) - - ;; If the state contains the start of the containing sexp we - ;; cache that position too, so that parse-partial-sexp in the - ;; next run has a bigger chance of starting at the same level - ;; as the target position and thus will get more good safe - ;; positions into the list. - (if (elt state 1) - (setq safe-pos (1+ (elt state 1)) - safe-pos-list (cons safe-pos safe-pos-list)))) - - (> (point) - (progn - ;; Skip syntactic ws afterwards so that we don't stop at the - ;; end of a comment if `skip-chars' is something like "^/". - (c-backward-syntactic-ws) - (point))))) + (goto-char limit) + nil) + t)) + (error + (goto-char (or limit (point-min))) + nil)) + + ;; The hit was outside the list at the start + ;; position. Go to the start of the list and exit. + (goto-char (1+ (elt state-2 1))) + nil)) + + ((c-beginning-of-macro limit) + ;; Inside a macro. + (if (< (point) + (or start-macro-beg + (setq start-macro-beg + (save-excursion + (goto-char start) + (c-beginning-of-macro limit) + (point))))) + t + + ;; It's inside the same macro we started in so it's + ;; a relevant match. + (goto-char pos) + nil)))))) + + (> (point) + (progn + ;; Skip syntactic ws afterwards so that we don't stop at the + ;; end of a comment if `skip-chars' is something like "^/". + (c-backward-syntactic-ws) + (point))))) ;; We might want to extend this with more useful return values in ;; the future. @@ -8426,6 +8437,7 @@ literal char-before-ip before-ws-ip char-after-ip macro-start in-macro-expr c-syntactic-context placeholder c-in-literal-cache step-type tmpsymbol keyword injava-inher special-brace-list tmp-pos + containing-< ;; The following record some positions for the containing ;; declaration block if we're directly within one: ;; `containing-decl-open' is the position of the open @@ -9040,7 +9052,7 @@ (back-to-indentation))) ;; FIXME: Should use c-add-stmt-syntax, but it's not yet ;; template aware. - (c-add-syntax 'template-args-cont (point))) + (c-add-syntax 'template-args-cont (point) placeholder)) ;; CASE 5D.4: perhaps a multiple inheritance line? ((and (c-major-mode-is 'c++-mode) @@ -9252,10 +9264,11 @@ ;; arglist that begins on the previous line. ((and c-recognize-<>-arglists (eq (char-before) ?<) + (setq placeholder (1- (point))) (not (and c-overloadable-operators-regexp (c-after-special-operator-id lim)))) (c-beginning-of-statement-1 (c-safe-position (point) paren-state)) - (c-add-syntax 'template-args-cont (c-point 'boi))) + (c-add-syntax 'template-args-cont (c-point 'boi) placeholder)) ;; CASE 5Q: we are at a statement within a macro. (macro-start @@ -9277,14 +9290,38 @@ ;; (CASE 6 has been removed.) + ;; CASE 19: line is an expression, not a statement, and is directly + ;; contained by a template delimiter. Most likely, we are in a + ;; template arglist within a statement. This case is based on CASE + ;; 7. At some point in the future, we may wish to create more + ;; syntactic symbols such as `template-intro', + ;; `template-cont-nonempty', etc., and distinguish between them as we + ;; do for `arglist-intro' etc. (2009-12-07). + ((and c-recognize-<>-arglists + (setq containing-< (c-up-list-backward indent-point containing-sexp)) + (eq (char-after containing-<) ?\<)) + (setq placeholder (c-point 'boi containing-<)) + (goto-char containing-sexp) ; Most nested Lbrace/Lparen (but not + ; '<') before indent-point. + (if (>= (point) placeholder) + (progn + (forward-char) + (skip-chars-forward " \t")) + (goto-char placeholder)) + (c-add-stmt-syntax 'template-args-cont (list containing-<) t + (c-most-enclosing-brace c-state-cache (point)) + paren-state)) + + ;; CASE 7: line is an expression, not a statement. Most ;; likely we are either in a function prototype or a function - ;; call argument list + ;; call argument list, or a template argument list. ((not (or (and c-special-brace-lists (save-excursion (goto-char containing-sexp) (c-looking-at-special-brace-list))) - (eq (char-after containing-sexp) ?{))) + (eq (char-after containing-sexp) ?{) + (eq (char-after containing-sexp) ?<))) (cond ;; CASE 7A: we are looking at the arglist closing paren. @@ -9381,7 +9418,7 @@ (c-forward-syntactic-ws) (point)) (c-point 'bonl))) - (goto-char containing-sexp) + (goto-char containing-sexp) ; paren opening the arglist (setq placeholder (c-point 'boi)) (if (and (c-safe (backward-up-list 1) t) (>= (point) placeholder)) diff -r 3ecd902dead9 -r 0a0832d4518d lisp/progmodes/cc-mode.el --- a/lisp/progmodes/cc-mode.el Thu Dec 10 14:24:42 2009 +0000 +++ b/lisp/progmodes/cc-mode.el Thu Dec 10 14:29:11 2009 +0000 @@ -541,19 +541,15 @@ (make-local-variable 'lookup-syntax-properties) (setq lookup-syntax-properties t))) - ;; Use this in Emacs 21 to avoid meddling with the rear-nonsticky + ;; Use this in Emacs 21+ to avoid meddling with the rear-nonsticky ;; property on each character. (when (boundp 'text-property-default-nonsticky) (make-local-variable 'text-property-default-nonsticky) - (let ((elem (assq 'syntax-table text-property-default-nonsticky))) - (if elem - (setcdr elem t) - (setq text-property-default-nonsticky - (cons '(syntax-table . t) - text-property-default-nonsticky)))) - (setq text-property-default-nonsticky - (cons '(c-type . t) - text-property-default-nonsticky))) + (mapc (lambda (tprop) + (unless (assq tprop text-property-default-nonsticky) + (setq text-property-default-nonsticky + (cons `(,tprop . t) text-property-default-nonsticky)))) + '(syntax-table category c-type))) ;; In Emacs 21 and later it's possible to turn off the ad-hoc ;; heuristic that open parens in column 0 are defun starters. Since