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 '<'.
This commit is contained in:
Alan Mackenzie 2009-12-10 14:29:11 +00:00
parent f1943c1ba5
commit d0fcee6698
2 changed files with 161 additions and 128 deletions

View file

@ -3743,6 +3743,57 @@ comment at the start of cc-engine.el for more info."
(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 @@ Note that this function might do hidden buffer changes. See the
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)
(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)
;; 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)))
(let ((pos (point)) state-2 pps-end-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
;; Don't stop inside a literal
((setq lit-beg (c-ssb-lit-begin))
(goto-char lit-beg)
t)
(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.
((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)))
(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
;; 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))
(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))
;; 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
((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)))))
;; 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 @@ comment at the start of cc-engine.el for more info."
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 @@ comment at the start of cc-engine.el for more info."
(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 @@ comment at the start of cc-engine.el for more info."
;; 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 @@ comment at the start of cc-engine.el for more info."
;; (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 @@ comment at the start of cc-engine.el for more info."
(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))

View file

@ -541,19 +541,15 @@ that requires a literal mode spec at compile time."
(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