diff options
Diffstat (limited to 'lisp/progmodes/cc-engine.el')
-rw-r--r-- | lisp/progmodes/cc-engine.el | 1698 |
1 files changed, 1075 insertions, 623 deletions
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 484624b8664..5d2e41ae575 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -1,4 +1,4 @@ -;;; cc-engine.el --- core syntax guessing engine for CC mode -*- coding: utf-8 -*- +;;; cc-engine.el --- core syntax guessing engine for CC mode -*- lexical-binding:t; coding: utf-8 -*- ;; Copyright (C) 1985, 1987, 1992-2021 Free Software Foundation, Inc. @@ -163,11 +163,14 @@ (defvar c-doc-line-join-re) (defvar c-doc-bright-comment-start-re) (defvar c-doc-line-join-end-ch) +(defvar c-syntactic-context) +(defvar c-syntactic-element) (cc-bytecomp-defvar c-min-syn-tab-mkr) (cc-bytecomp-defvar c-max-syn-tab-mkr) (cc-bytecomp-defun c-clear-syn-tab) (cc-bytecomp-defun c-clear-string-fences) (cc-bytecomp-defun c-restore-string-fences) +(cc-bytecomp-defun c-remove-string-fences) ;; Make declarations for all the `c-lang-defvar' variables in cc-langs. @@ -735,6 +738,7 @@ comment at the start of cc-engine.el for more info." '(setq stack (cons (cons state saved-pos) stack))) (defmacro c-bos-pop-state (&optional do-if-done) + (declare (debug t)) `(if (setq state (car (car stack)) saved-pos (cdr (car stack)) stack (cdr stack)) @@ -759,6 +763,7 @@ comment at the start of cc-engine.el for more info." (goto-char pos) (setq sym nil))) (defmacro c-bos-save-error-info (missing got) + (declare (debug t)) `(setq saved-pos (vector pos ,missing ,got))) (defmacro c-bos-report-error () '(unless noerror @@ -1184,6 +1189,15 @@ comment at the start of cc-engine.el for more info." ;; suitable error. (setq pre-stmt-found t) (throw 'loop nil)) + ;; Handle C++'s `constexpr', etc. + (if (save-excursion + (and (looking-at c-block-stmt-hangon-key) + (progn + (c-backward-syntactic-ws lim) + (c-safe (c-backward-sexp) t)) + (looking-at c-block-stmt-2-key) + (setq pos (point)))) + (goto-char pos)) (cond ;; Have we moved into a macro? ((and (not macro-start) @@ -1860,51 +1874,51 @@ comment at the start of cc-engine.el for more info." ; (setq in-face (point))) ; (not (eobp))))))) -(defmacro c-debug-sws-msg (&rest args) - (ignore args) +(defmacro c-debug-sws-msg (&rest _args) + ;; (declare (debug t)) ;;`(message ,@args) ) (defmacro c-put-is-sws (beg end) ;; This macro does a hidden buffer change. + (declare (debug t)) `(let ((beg ,beg) (end ,end)) (put-text-property beg end 'c-is-sws t) ,@(when (facep 'c-debug-is-sws-face) '((c-debug-add-face beg end 'c-debug-is-sws-face))))) -(def-edebug-spec c-put-is-sws t) (defmacro c-put-in-sws (beg end) ;; This macro does a hidden buffer change. + (declare (debug t)) `(let ((beg ,beg) (end ,end)) (put-text-property beg end 'c-in-sws t) ,@(when (facep 'c-debug-is-sws-face) '((c-debug-add-face beg end 'c-debug-in-sws-face))))) -(def-edebug-spec c-put-in-sws t) (defmacro c-remove-is-sws (beg end) ;; This macro does a hidden buffer change. + (declare (debug t)) `(let ((beg ,beg) (end ,end)) (remove-text-properties beg end '(c-is-sws nil)) ,@(when (facep 'c-debug-is-sws-face) '((c-debug-remove-face beg end 'c-debug-is-sws-face))))) -(def-edebug-spec c-remove-is-sws t) (defmacro c-remove-in-sws (beg end) ;; This macro does a hidden buffer change. + (declare (debug t)) `(let ((beg ,beg) (end ,end)) (remove-text-properties beg end '(c-in-sws nil)) ,@(when (facep 'c-debug-is-sws-face) '((c-debug-remove-face beg end 'c-debug-in-sws-face))))) -(def-edebug-spec c-remove-in-sws t) (defmacro c-remove-is-and-in-sws (beg end) ;; This macro does a hidden buffer change. + (declare (debug t)) `(let ((beg ,beg) (end ,end)) (remove-text-properties beg end '(c-is-sws nil c-in-sws nil)) ,@(when (facep 'c-debug-is-sws-face) '((c-debug-remove-face beg end 'c-debug-is-sws-face) (c-debug-remove-face beg end 'c-debug-in-sws-face))))) -(def-edebug-spec c-remove-is-and-in-sws t) ;; The type of literal position `end' is in a `before-change-functions' ;; function - one of `c', `c++', `pound', `noise', `attribute' or nil (but NOT @@ -2665,7 +2679,7 @@ comment at the start of cc-engine.el for more info." ;; One of the above "near" caches is associated with each of these functions. ;; ;; When searching this cache, these functions first seek an exact match, then -;; a "close" match from the assiciated near cache. If neither of these +;; a "close" match from the associated near cache. If neither of these ;; succeed, the nearest preceding entry in the far cache is used. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2708,9 +2722,9 @@ comment at the start of cc-engine.el for more info." ;; two char construct (such as a comment opener or an escaped character).) (if (and (consp elt) (>= (length elt) 3)) ;; Inside a string or comment - (let ((depth 0) (containing nil) (last nil) + (let ((depth 0) (containing nil) in-string in-comment - (min-depth 0) com-style com-str-start (intermediate nil) + (min-depth 0) com-style com-str-start (char-1 (nth 3 elt)) ; first char of poss. 2-char construct (pos (car elt)) (type (cadr elt))) @@ -2727,14 +2741,13 @@ comment at the start of cc-engine.el for more info." (1- pos) pos)) (if (memq 'pps-extended-state c-emacs-features) - (list depth containing last + (list depth containing nil in-string in-comment nil min-depth com-style com-str-start - intermediate nil) - (list depth containing last + nil nil) + (list depth containing nil in-string in-comment nil - min-depth com-style com-str-start - intermediate))) + min-depth com-style com-str-start nil))) ;; Not in a string or comment. (if (memq 'pps-extended-state c-emacs-features) @@ -3128,21 +3141,21 @@ comment at the start of cc-engine.el for more info." (setq base far-base s far-s end nil)))) - (when - (or - (and (> here base) (null end)) - (null (nth 8 s)) - (and end (>= here end)) - (not - (or - (and (nth 3 s) ; string - (not (eq (char-before here) ?\\))) - (and (nth 4 s) (not (nth 7 s)) ; Block comment - (not (memq (char-before here) - c-block-comment-awkward-chars))) - (and (nth 4 s) (nth 7 s) ; Line comment - (not (memq (char-before here) '(?\\ ?\n))))))) + (cond + ((or (and (> here base) (null end)) + (null (nth 8 s)) + (and end (>= here end))) (setq s (parse-partial-sexp base here nil nil s))) + ((or (and (nth 3 s) ; string + (eq (char-before here) ?\\)) + (and (nth 4 s) (not (nth 7 s)) ; block comment + (memq (char-before here) c-block-comment-awkward-chars)) + (and (nth 4 s) (nth 7 s) ; line comment + (memq (char-before here) '(?\\ ?\n)))) + (setq s + (if (>= here base) + (parse-partial-sexp base here nil nil s) + (parse-partial-sexp (nth 8 s) here))))) (cond ((or (nth 3 s) (and (nth 4 s) @@ -3507,6 +3520,7 @@ mhtml-mode." (defmacro c-state-cache-top-lparen (&optional cache) ;; Return the address of the top left brace/bracket/paren recorded in CACHE ;; (default `c-state-cache') (or nil). + (declare (debug t)) (let ((cash (or cache 'c-state-cache))) `(if (consp (car ,cash)) (caar ,cash) @@ -3515,6 +3529,7 @@ mhtml-mode." (defmacro c-state-cache-top-paren (&optional cache) ;; Return the address of the latest brace/bracket/paren (whether left or ;; right) recorded in CACHE (default `c-state-cache') or nil. + (declare (debug t)) (let ((cash (or cache 'c-state-cache))) `(if (consp (car ,cash)) (cdar ,cash) @@ -3523,6 +3538,7 @@ mhtml-mode." (defmacro c-state-cache-after-top-paren (&optional cache) ;; Return the position just after the latest brace/bracket/paren (whether ;; left or right) recorded in CACHE (default `c-state-cache') or nil. + (declare (debug t)) (let ((cash (or cache 'c-state-cache))) `(if (consp (car ,cash)) (cdar ,cash) @@ -3784,12 +3800,14 @@ mhtml-mode." (point))) (bra ; Position of "{". ;; Don't start scanning in the middle of a CPP construct unless - ;; it contains HERE - these constructs, in Emacs, are "commented - ;; out" with category properties. - (if (eq (c-get-char-property macro-start-or-from 'category) - 'c-cpp-delimiter) - macro-start-or-from - from)) + ;; it contains HERE. + (if (and (not (eq macro-start-or-from from)) + (< macro-start-or-from here) ; Might not be needed. + (progn (goto-char macro-start-or-from) + (c-end-of-macro) + (>= (point) here))) + from + macro-start-or-from)) ce) ; Position of "}" (or upper-lim (setq upper-lim from)) @@ -4319,38 +4337,29 @@ mhtml-mode." (setq c-state-nonlit-pos-cache-limit (1- here))) (c-truncate-lit-pos-cache here) - ;; `c-state-cache': - ;; Case 1: if `here' is in a literal containing point-min, everything - ;; becomes (or is already) nil. - (if (or (null c-state-cache-good-pos) - (< here (c-state-get-min-scan-pos))) - (setq c-state-cache nil - c-state-cache-good-pos nil - c-state-min-scan-pos nil) - - ;; Truncate `c-state-cache' and set `c-state-cache-good-pos' to a value - ;; below `here'. To maintain its consistency, we may need to insert a new - ;; brace pair. - (let ((here-bol (c-point 'bol here)) - too-high-pa ; recorded {/(/[ next above or just below here, or nil. - dropped-cons) ; was the last removed element a brace pair? - ;; The easy bit - knock over-the-top bits off `c-state-cache'. - (while (and c-state-cache - (>= (c-state-cache-top-paren) here)) - (setq dropped-cons (consp (car c-state-cache)) - too-high-pa (c-state-cache-top-lparen) - c-state-cache (cdr c-state-cache))) - - ;; Do we need to add in an earlier brace pair, having lopped one off? - (if (and dropped-cons - (<= too-high-pa here)) - (c-append-lower-brace-pair-to-state-cache too-high-pa here here-bol)) - (if (and c-state-cache-good-pos (< here c-state-cache-good-pos)) - (setq c-state-cache-good-pos - (or (save-excursion - (goto-char here) - (c-literal-start)) - here))))) + (cond + ;; `c-state-cache': + ;; Case 1: if `here' is in a literal containing point-min, everything + ;; becomes (or is already) nil. + ((or (null c-state-cache-good-pos) + (< here (c-state-get-min-scan-pos))) + (setq c-state-cache nil + c-state-cache-good-pos nil + c-state-min-scan-pos nil)) + + ;; Case 2: `here' is below `c-state-cache-good-pos', so we need to amend + ;; the entire `c-state-cache' data. + ((< here c-state-cache-good-pos) + (let* ((res (c-remove-stale-state-cache-backwards here)) + (good-pos (car res)) + (scan-backward-pos (cadr res)) + (scan-forward-p (car (cddr res)))) + (if scan-backward-pos + (c-append-lower-brace-pair-to-state-cache scan-backward-pos here)) + (setq c-state-cache-good-pos + (if scan-forward-p + (c-append-to-state-cache good-pos here) + good-pos))))) ;; The brace-pair desert marker: (when (car c-state-brace-pair-desert) @@ -4484,6 +4493,7 @@ mhtml-mode." (defmacro c-state-maybe-marker (place marker) ;; If PLACE is non-nil, return a marker marking it, otherwise nil. ;; We (re)use MARKER. + (declare (debug (form symbolp))) `(let ((-place- ,place)) (and -place- (or ,marker (setq ,marker (make-marker))) @@ -5970,6 +5980,7 @@ comment at the start of cc-engine.el for more info." ; spots and the preceding token end.") (defmacro c-debug-put-decl-spot-faces (match-pos decl-pos) + (declare (debug t)) (when (facep 'c-debug-decl-spot-face) `(c-save-buffer-state ((match-pos ,match-pos) (decl-pos ,decl-pos)) (c-debug-add-face (max match-pos (point-min)) decl-pos @@ -5977,6 +5988,7 @@ comment at the start of cc-engine.el for more info." (c-debug-add-face decl-pos (min (1+ decl-pos) (point-max)) 'c-debug-decl-spot-face)))) (defmacro c-debug-remove-decl-spot-faces (beg end) + (declare (debug t)) (when (facep 'c-debug-decl-spot-face) `(c-save-buffer-state () (c-debug-remove-face ,beg ,end 'c-debug-decl-spot-face) @@ -6931,8 +6943,10 @@ comment at the start of cc-engine.el for more info." (c-go-list-forward)) (when (equal (c-get-char-property (1- (point)) 'syntax-table) c->-as-paren-syntax) ; should always be true. - (c-unmark-<->-as-paren (1- (point)))) - (c-unmark-<->-as-paren pos)))) + (c-unmark-<->-as-paren (1- (point))) + (c-truncate-lit-pos-cache (1- (point)))) + (c-unmark-<->-as-paren pos) + (c-truncate-lit-pos-cache pos)))) (defun c-clear->-pair-props (&optional pos) ;; POS (default point) is at a > character. If it is marked with @@ -6948,8 +6962,10 @@ comment at the start of cc-engine.el for more info." (c-go-up-list-backward)) (when (equal (c-get-char-property (point) 'syntax-table) c-<-as-paren-syntax) ; should always be true. - (c-unmark-<->-as-paren (point))) - (c-unmark-<->-as-paren pos)))) + (c-unmark-<->-as-paren (point)) + (c-truncate-lit-pos-cache (point))) + (c-unmark-<->-as-paren pos) + (c-truncate-lit-pos-cache pos)))) (defun c-clear-<>-pair-props (&optional pos) ;; POS (default point) is at a < or > character. If it has an @@ -6982,7 +6998,8 @@ comment at the start of cc-engine.el for more info." (equal (c-get-char-property (1- (point)) 'syntax-table) c->-as-paren-syntax)) ; should always be true. (c-unmark-<->-as-paren (1- (point))) - (c-unmark-<->-as-paren pos)) + (c-unmark-<->-as-paren pos) + (c-truncate-lit-pos-cache pos)) t))) (defun c-clear->-pair-props-if-match-before (lim &optional pos) @@ -7003,6 +7020,7 @@ comment at the start of cc-engine.el for more info." (equal (c-get-char-property (point) 'syntax-table) c-<-as-paren-syntax)) ; should always be true. (c-unmark-<->-as-paren (point)) + (c-truncate-lit-pos-cache (point)) (c-unmark-<->-as-paren pos)) t))) @@ -7150,554 +7168,954 @@ comment at the start of cc-engine.el for more info." (goto-char c-new-END))))) -;; Functions to handle C++ raw strings. +;; Handling of CC Mode multi-line strings. ;; -;; A valid C++ raw string looks like -;; R"<id>(<contents>)<id>" -;; , where <id> is an identifier from 0 to 16 characters long, not containing -;; spaces, control characters, or left/right paren. <contents> can include -;; anything which isn't the terminating )<id>", including new lines, "s, -;; parentheses, etc. +;; By a "multi-line string" is meant a string opened by a "decorated" +;; double-quote mark, and which can continue over several lines without the +;; need to escape the newlines, terminating at a closer, a possibly +;; "decorated" double-quote mark. The string can usually contain double +;; quotes without them being quoted, whether or not backslashes quote the +;; following character being a matter of configuration. ;; -;; CC Mode handles C++ raw strings by the use of `syntax-table' text +;; CC Mode handles multi-line strings by the use of `syntax-table' text ;; properties as follows: ;; -;; (i) On a validly terminated raw string, no `syntax-table' text properties -;; are applied to the opening and closing delimiters, but any " in the -;; contents is given the property value "punctuation" (`(1)') to prevent it -;; interacting with the "s in the delimiters. +;; (i) On a validly terminated ml string, syntax-table text-properties are +;; applied as needed to the opener, so that the " character in the opener +;; (or (usually) the first of them if there are several) retains its normal +;; syntax, and any other characters with obtrusive syntax are given +;; "punctuation" '(1) properties. Similarly, the " character in the closer +;; retains its normal syntax, and characters with obtrusive syntax are +;; "punctuated out" as before. ;; -;; The font locking routine `c-font-lock-raw-strings' (in cc-fonts.el) -;; recognizes valid raw strings, and fontifies the delimiters (apart from -;; the parentheses) with the default face and the parentheses and the -;; <contents> with font-lock-string-face. +;; The font locking routine `c-font-lock-ml-strings' (in cc-fonts.el) +;; recognizes validly terminated ml strings and fontifies (typically) the +;; innermost character of each delimiter in font-lock-string-face and the +;; rest of those delimiters in the default face. The contents, of course, +;; are in font-lock-string-face. ;; -;; (ii) A valid, but unterminated, raw string opening delimiter gets the -;; "punctuation" value (`(1)') of the `syntax-table' text property, and the -;; open parenthesis gets the "string fence" value (`(15)'). When such a -;; delimiter is found, no attempt is made in any way to "correct" any text -;; properties after the delimiter. +;; (ii) A valid, but unterminated, ml string's opening delimiter gets the +;; "punctuation" value (`(1)') of the `syntax-table' text property on its ", +;; and the last char of the opener gets the "string fence" value '(15). +;; (The latter takes precedence over the former.) When such a delimiter is +;; found, no attempt is made in any way to "correct" any text properties +;; after the delimiter. ;; -;; `c-font-lock-raw-strings' puts c-font-lock-warning-face on the entire -;; unmatched opening delimiter (from the R up to the open paren), and allows -;; the rest of the buffer to get font-lock-string-face, caused by the -;; unmatched "string fence" `syntax-table' text property value. +;; `c-font-lock-ml-strings' puts c-font-lock-warning-face on the entire +;; unmatched opening delimiter, and allows the tail of the buffer to get +;; font-lock-string-face, caused by the unmatched "string fence" +;; `syntax-table' text property value. ;; -;; (iii) Inside a macro, a valid raw string is handled as in (i). An -;; unmatched opening delimiter is handled slightly differently. In addition -;; to the "punctuation" and "string fence" properties on the delimiter, -;; another "string fence" `syntax-table' property is applied to the last -;; possible character of the macro before the terminating linefeed (if there -;; is such a character after the "("). This "last possible" character is +;; (iii) Inside a macro, a valid ml string is handled as in (i). An unmatched +;; opening delimiter is handled slightly differently. In addition to the +;; "punctuation" and "string fence" properties on the delimiter, another +;; "string fence" `syntax-table' property is applied to the last possible +;; character of the macro before the terminating linefeed (if there is such +;; a character after the delimiter). This "last possible" character is ;; never a backslash escaping the end of line. If the character preceding ;; this "last possible" character is itself a backslash, this preceding -;; character gets a "punctuation" `syntax-table' value. If the "(" is -;; already at the end of the macro, it gets the "punctuation" value, and no -;; "string fence"s are used. +;; character gets a "punctuation" `syntax-table' value. If the last +;; character of the closing delimiter is already at the end of the macro, it +;; gets the "punctuation" value, and no "string fence"s are used. ;; ;; The effect on the fontification of either of these tactics is that the ;; rest of the macro (if any) after the "(" gets font-lock-string-face, but ;; the rest of the file is fontified normally. -;; The values of the function `c-raw-string-pos' at before-change-functions' -;; BEG and END. -(defvar c-old-beg-rs nil) -(defvar c-old-end-rs nil) -;; Whether a buffer change has disrupted or will disrupt the terminating id of -;; a raw string. -(defvar c-raw-string-end-delim-disrupted nil) - -(defun c-raw-string-pos () - ;; Get POINT's relationship to any containing raw string. - ;; If point isn't in a raw string, return nil. - ;; Otherwise, return the following list: - ;; - ;; (POS B\" B\( E\) E\") - ;; - ;; , where POS is the symbol `open-delim' if point is in the opening - ;; delimiter, the symbol `close-delim' if it's in the closing delimiter, and - ;; nil if it's in the string body. B\", B\(, E\), E\" are the positions of - ;; the opening and closing quotes and parentheses of a correctly terminated - ;; raw string. (N.B.: E\) and E\" are NOT on the "outside" of these - ;; characters.) If the raw string is not terminated, E\) and E\" are set to +(defun c-ml-string-make-closer-re (_opener) + "Return c-ml-string-any-closer-re. + +This is a suitable language specific value of +`c-make-ml-string-closer-re-function' for most languages with +multi-line strings (but not C++, for example)." + c-ml-string-any-closer-re) + +(defun c-ml-string-make-opener-re (_closer) + "Return c-ml-string-opener-re. + +This is a suitable language specific value of +`c-make-ml-string-opener-re-function' for most languages with +multi-line strings (but not C++, for example)." + c-ml-string-opener-re) + +(defun c-c++-make-ml-string-closer-re (opener) + "Construct a regexp for a C++ raw string closer matching OPENER." + (concat "\\()" (regexp-quote (substring opener 2 -1)) "\\(\"\\)\\)")) + +(defun c-c++-make-ml-string-opener-re (closer) + "Construct a regexp for a C++ raw string opener matching CLOSER." + (concat "\\(R\\(\"\\)" (regexp-quote (substring closer 1 -1)) "(\\)")) + +;; The positions of various components of mult-line strings surrounding BEG, +;; END and (1- BEG) (of before-change-functions) as returned by +;; `c-ml-string-delims-around-point'. +(defvar c-old-beg-ml nil) +(defvar c-old-1-beg-ml nil) ; only non-nil when `c-old-beg-ml' is nil. +(defvar c-old-end-ml nil) +;; The values of the function `c-position-wrt-ml-delims' at +;; before-change-function's BEG and END. +(defvar c-beg-pos nil) +(defvar c-end-pos nil) +;; Whether a buffer change has disrupted or will disrupt the terminator of an +;; multi-line string. +(defvar c-ml-string-end-delim-disrupted nil) + +(defun c-depropertize-ml-string-delims (string-delims) + ;; Remove any syntax-table text properties from the multi-line string + ;; delimiters specified by STRING-DELIMS, the output of + ;; `c-ml-string-delims-around-point'. + (let (found) + (if (setq found (c-clear-char-properties (caar string-delims) + (cadar string-delims) + 'syntax-table)) + (c-truncate-lit-pos-cache found)) + (when (cdr string-delims) + (if (setq found (c-clear-char-properties (cadr string-delims) + (caddr string-delims) + 'syntax-table)) + (c-truncate-lit-pos-cache found))))) + +(defun c-get-ml-closer (open-delim) + ;; Return the closer, a three element dotted list of the closer's start, its + ;; end and the position of the double quote, matching the given multi-line + ;; string OPENER, also such a three element dotted list. Otherwise return + ;; nil. All pertinent syntax-table text properties must be in place. + (save-excursion + (goto-char (cadr open-delim)) + (and (not (equal (c-get-char-property (1- (point)) 'syntax-table) + '(15))) + (re-search-forward (funcall c-make-ml-string-closer-re-function + (buffer-substring-no-properties + (car open-delim) (cadr open-delim))) + nil t) + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2)))))) + +(defun c-ml-string-opener-around-point () + ;; If point is inside an ml string opener, return a dotted list of the start + ;; and end of that opener, and the position of its double-quote. That list + ;; will not include any "context characters" before or after the opener. If + ;; an opener is found, the match-data will indicate it, with (match-string + ;; 1) being the entire delimiter, and (match-string 2) the "main" double + ;; quote. Otherwise the match-data is undefined. + (let ((here (point)) found) + (goto-char (max (- here (1- c-ml-string-max-opener-len)) (point-min))) + (while + (and + (setq found + (search-forward-regexp + c-ml-string-opener-re + (min (+ here (1- c-ml-string-max-opener-len)) (point-max)) + 'bound)) + (<= (match-end 1) here))) + (prog1 + (and found + (< (match-beginning 1) here) + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2)))) + (goto-char here)))) + +(defun c-ml-string-opener-intersects-region (&optional start finish) + ;; If any part of the region [START FINISH] is inside an ml-string opener, + ;; return a dotted list of the start, end and double-quote position of that + ;; opener. That list wlll not include any "context characters" before or + ;; after the opener. If an opener is found, the match-data will indicate + ;; it, with (match-string 1) being the entire delimiter, and (match-string + ;; 2) the "main" double-quote. Otherwise, the match-data is undefined. + ;; Both START and FINISH default to point. FINISH may not be at an earlier + ;; buffer position than START. + (let ((here (point)) found) + (or finish (setq finish (point))) + (or start (setq start (point))) + (goto-char (max (- start (1- c-ml-string-max-opener-len)) (point-min))) + (while + (and + (setq found + (search-forward-regexp + c-ml-string-opener-re + (min (+ finish (1- c-ml-string-max-opener-len)) (point-max)) + 'bound)) + (<= (match-end 1) start))) + (prog1 + (and found + (< (match-beginning 1) finish) + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2)))) + (goto-char here)))) + +(defun c-ml-string-opener-at-or-around-point (&optional position) + ;; If POSITION (default point) is at or inside an ml string opener, return a + ;; dotted list of the start and end of that opener, and the position of the + ;; double-quote in it. That list will not include any "context characters" + ;; before or after the opener. + (let ((here (point)) + found) + (or position (setq position (point))) + (goto-char (max (- position (1- c-ml-string-max-opener-len)) (point-min))) + (while + (and + (setq found + (search-forward-regexp + c-ml-string-opener-re + (min (+ position c-ml-string-max-opener-len) (point-max)) + 'bound)) + (<= (match-end 1) position))) + (prog1 + (and found + (<= (match-beginning 1) position) + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2)))) + (goto-char here)))) + +(defun c-ml-string-back-to-neutral (opening-point) + ;; Given OPENING-POINT, the position of the start of a multiline string + ;; opening delimiter, move point back to a neutral position within the ml + ;; string. It is assumed that point is within the innards of or the closing + ;; delimiter of string opened by OPEN-DELIM. + (let ((opener-end (save-excursion + (goto-char opening-point) + (looking-at c-ml-string-opener-re) + (match-end 1)))) + (if (not c-ml-string-back-closer-re) + (goto-char (max (c-point 'boll) opener-end)) + (re-search-backward c-ml-string-back-closer-re + (max opener-end + (c-point 'eopl)) + 'bound)))) + +(defun c-ml-string-in-end-delim (beg end open-delim) + ;; If the region (BEG END) intersects or touches a possible multiline string + ;; terminator, return a cons of the position of the start and end of the + ;; first such terminator. The syntax-table text properties must be in a + ;; consistent state when using this function. OPEN-DELIM is the three + ;; element dotted list of the start, end, and double quote position of the + ;; multiline string opener that BEG is in, or nil if it isn't in one. + (save-excursion + (goto-char beg) + (when open-delim + ;; If BEG is in an opener, move back to a position we know to be "safe". + (if (<= beg (cadr open-delim)) + (goto-char (cadr open-delim)) + (c-ml-string-back-to-neutral (car open-delim)))) + + (let (saved-match-data) + (or + ;; If we might be in the middle of "context" bytes at the start of a + ;; closer, move to after the closer. + (and c-ml-string-back-closer-re + (looking-at c-ml-string-any-closer-re) + (eq (c-in-literal) 'string) + (setq saved-match-data (match-data)) + (goto-char (match-end 0))) + + ;; Otherwise, move forward over closers while we haven't yet reached END, + ;; until we're after BEG. + (progn + (while + (let (found) + (while ; Go over a single real closer. + (and + (search-forward-regexp + c-ml-string-any-closer-re + (min (+ end c-ml-string-max-closer-len-no-leader) + (point-max)) + t) + (save-excursion + (goto-char (match-end 1)) + (if (c-in-literal) ; a psuedo closer. + t + (setq saved-match-data (match-data)) + (setq found t) + nil)))) + (and found + (<= (point) beg)) + ;; (not (save-excursion + ;; (goto-char (match-beginning 2)) + ;; (c-literal-start))) + )))) + (set-match-data saved-match-data)) + + ;; Test whether we've found the sought closing delimiter. + (unless (or (null (match-data)) + (and (not (eobp)) + (<= (point) beg)) + (> (match-beginning 0) beg) + (progn (goto-char (match-beginning 2)) + (not (c-literal-start)))) + (cons (match-beginning 1) (match-end 1))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun c-ml-string-delims-around-point () + ;; Get POINT's relationship to any containing multi-line string or such a + ;; multi-line string which point is at the end of. + ;; + ;; If point isn't thus situated, return nil. + ;; Otherwise return the following cons: + ;; + ;; (OPENER . CLOSER) + ;; + ;; , where each of OPENER and CLOSER is a dotted list of the form + ;; + ;; (START-DELIM END-DELIM . QUOTE-POSITION) + ;; + ;; , the bounds of the delimiters and the buffer position of the ?" in the + ;; delimiter. If the ml-string is not validly terminated, CLOSER is instead ;; nil. ;; ;; Note: this function is dependent upon the correct syntax-table text ;; properties being set. - (let ((state (c-semi-pp-to-literal (point))) - open-quote-pos open-paren-pos close-paren-pos close-quote-pos id) - (save-excursion - (when - (and - (cond - ((null (cadr state)) - (or (eq (char-after) ?\") - (search-backward "\"" (max (- (point) 17) (point-min)) t))) - ((and (eq (cadr state) 'string) - (goto-char (nth 2 state)) - (cond - ((eq (char-after) ?\")) - ((eq (char-after) ?\() - (let ((here (point))) - (goto-char (max (- (point) 18) (point-min))) - (while - (and - (search-forward-regexp - c-c++-raw-string-opener-re - (1+ here) 'limit) - (< (point) here))) - (and (eq (point) (1+ here)) - (match-beginning 1) - (goto-char (1- (match-beginning 1))))))) - (not (bobp))))) - (c-at-c++-raw-string-opener)) - (setq open-quote-pos (point) - open-paren-pos (match-end 1) - id (match-string-no-properties 1)) - (goto-char (1+ open-paren-pos)) - (when (and (not (c-get-char-property open-paren-pos 'syntax-table)) - (search-forward (concat ")" id "\"") nil t)) - (setq close-paren-pos (match-beginning 0) - close-quote-pos (1- (point)))))) - (and open-quote-pos - (list - (cond - ((<= (point) open-paren-pos) - 'open-delim) - ((and close-paren-pos - (> (point) close-paren-pos)) - 'close-delim) - (t nil)) - open-quote-pos open-paren-pos close-paren-pos close-quote-pos)))) - -(defun c-raw-string-in-end-delim (beg end) - ;; If the region (BEG END) intersects a possible raw string terminator, - ;; return a cons of the position of the ) and the position of the " in the - ;; first one found. - (save-excursion - (goto-char (max (- beg 17) (point-min))) - (while - (and - (search-forward-regexp ")\\([^ ()\\\n\r\t]\\{0,16\\}\\)\"" - (min (+ end 17) (point-max)) t) - (<= (point) beg))) - (unless (or (<= (point) beg) - (>= (match-beginning 0) end)) - (cons (match-beginning 0) (match-end 1))))) - -(defun c-depropertize-raw-string (id open-quote open-paren bound) - ;; Point is immediately after a raw string opening delimiter. Remove any - ;; `syntax-table' text properties associated with the delimiter (if it's - ;; unmatched) or the raw string. - ;; - ;; ID, a string, is the delimiter's identifier. OPEN-QUOTE and OPEN-PAREN - ;; are the buffer positions of the delimiter's components. BOUND is the - ;; bound for searching for a matching closing delimiter; it is usually nil, - ;; but if we're inside a macro, it's the end of the macro (i.e. just before - ;; the terminating \n). - ;; - ;; Point is moved to after the (terminated) raw string, or left after the - ;; unmatched opening delimiter, as the case may be. The return value is of - ;; no significance. - (let ((open-paren-prop (c-get-char-property open-paren 'syntax-table)) - first) - ;; If the delimiter is "unclosed", or sombody's used " in their id, clear - ;; the 'syntax-table property from all of them. - (setq first (c-clear-char-property-with-value-on-char - open-quote open-paren 'syntax-table '(1) ?\")) - (if first (c-truncate-lit-pos-cache first)) + (let ((here (point)) + (state (c-semi-pp-to-literal (point))) + open-dlist close-dlist ret found opener) (cond - ((null open-paren-prop) - ;; Should be a terminated raw string... - (when (search-forward (concat ")" id "\"") nil t) - ;; Yes, it is. :-) - ;; Clear any '(1)s from "s in the identifier. - (setq first (c-clear-char-property-with-value-on-char - (1+ (match-beginning 0)) (1- (match-end 0)) - 'syntax-table '(1) ?\")) - (if first (c-truncate-lit-pos-cache first)) - ;; Clear any random `syntax-table' text properties from the contents. - (let* ((closing-paren (match-beginning 0)) - (first-st - (and - (< (1+ open-paren) closing-paren) - (or - (and (c-get-char-property (1+ open-paren) 'syntax-table) - (1+ open-paren)) - (and - (setq first - (c-next-single-property-change - (1+ open-paren) 'syntax-table nil closing-paren)) - (< first closing-paren) - first))))) - (when first-st - (c-clear-char-properties first-st (match-beginning 0) - 'syntax-table) - (c-truncate-lit-pos-cache first-st)) - (when (c-get-char-property (1- (match-end 0)) 'syntax-table) - ;; Was previously an unterminated (ordinary) string - (save-excursion - (goto-char (1- (match-end 0))) - (when (c-safe (c-forward-sexp)) ; to '(1) at EOL. - (c-clear-char-property (1- (point)) 'syntax-table)) - (c-clear-char-property (1- (match-end 0)) 'syntax-table) - (c-truncate-lit-pos-cache (1- (match-end 0)))))))) - ((or (and (equal open-paren-prop '(15)) (null bound)) - (equal open-paren-prop '(1))) - ;; An unterminated raw string either not in a macro, or in a macro with - ;; the open parenthesis right up against the end of macro - (c-clear-char-property open-quote 'syntax-table) - (c-truncate-lit-pos-cache open-quote) - (c-clear-char-property open-paren 'syntax-table)) - (t - ;; An unterminated string in a macro, with at least one char after the - ;; open paren - (c-clear-char-property open-quote 'syntax-table) - (c-truncate-lit-pos-cache open-quote) - (c-clear-char-property open-paren 'syntax-table) - (c-clear-char-property-with-value (1+ open-paren) bound 'syntax-table - '(15)))))) - -(defun c-depropertize-raw-strings-in-region (start finish) - ;; Remove any `syntax-table' text properties associated with C++ raw strings - ;; contained in the region (START FINISH). Point is undefined at entry and - ;; exit, and the return value has no significance. - (goto-char start) - (while (and (< (point) finish) - (re-search-forward - (concat "\\(" ; 1 - c-anchored-cpp-prefix ; 2 - "\\)\\|\\(" ; 3 - c-c++-raw-string-opener-re ; 4 - "\\)") - finish t)) - (when (save-excursion - (goto-char (match-beginning 0)) (not (c-in-literal))) - (if (match-beginning 4) ; the id - ;; We've found a raw string - (c-depropertize-raw-string - (match-string-no-properties 4) ; id - (1+ (match-beginning 3)) ; open quote - (match-end 4) ; open paren - nil) ; bound - ;; We've found a CPP construct. Search for raw strings within it. - (goto-char (match-beginning 2)) ; the "#" - (c-end-of-macro) - (let ((eom (point))) - (goto-char (match-end 2)) ; after the "#". - (while (and (< (point) eom) - (c-syntactic-re-search-forward - c-c++-raw-string-opener-re eom t)) - (c-depropertize-raw-string - (match-string-no-properties 1) ; id - (1+ (match-beginning 0)) ; open quote - (match-end 1) ; open paren - eom))))))) ; bound. - -(defun c-before-change-check-raw-strings (beg end) - ;; This function clears `syntax-table' text properties from C++ raw strings - ;; whose delimiters are about to change in the region (c-new-BEG c-new-END). - ;; BEG and END are the standard arguments supplied to any before-change - ;; function. + ((or + ;; Is HERE between the start of an opener and the "? + (and (null (cadr state)) + (progn + ;; Search for the start of the opener. + (goto-char (max (- (point) (1- c-ml-string-max-opener-len)) + (point-min))) + (setq found nil) + ;; In the next loop, skip over any complete ml strings, or an ml + ;; string opener which is in a macro not containing HERE, or an + ;; apparent "opener" which is in a comment or string. + (while + (and (re-search-forward c-ml-string-opener-re + (+ here (1- c-ml-string-max-opener-len)) + t) + (< (match-beginning 1) here) + (or + (save-excursion + (goto-char (match-beginning 1)) + (or (c-in-literal) + (and (c-beginning-of-macro) + (< (progn (c-end-of-macro) (point)) + here)))) + (and + (setq found (match-beginning 1)) + (<= (point) here) + (save-match-data + (re-search-forward + (funcall c-make-ml-string-closer-re-function + (match-string-no-properties 1)) + here t)) + (<= (point) here)))) + (setq found nil)) + found)) + ;; Is HERE after the "? + (and (eq (cadr state) 'string) + (goto-char (nth 2 state)) + (c-ml-string-opener-at-or-around-point))) + (setq open-dlist (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2)))) + (goto-char (cadr open-dlist)) + (setq ret + (cons open-dlist + (if (re-search-forward + (funcall c-make-ml-string-closer-re-function + (match-string-no-properties 1)) + nil t) + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2))) + nil))) + (goto-char here) + ret) + ;; Is HERE between the " and the end of the closer? + ((and (null (cadr state)) + (progn + (if (null c-ml-string-back-closer-re) + (goto-char (max (- here (1- c-ml-string-max-closer-len)) + (point-min))) + (goto-char here) + (re-search-backward c-ml-string-back-closer-re nil t)) + (re-search-forward c-ml-string-any-closer-re + (+ here -1 c-ml-string-max-closer-len-no-leader) + t)) + (>= (match-end 1) here) + (<= (match-end 2) here) + (setq close-dlist (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2)))) + (goto-char (car close-dlist)) + (setq state (c-semi-pp-to-literal (point))) + (eq (cadr state) 'string) + (goto-char (nth 2 state)) + (setq opener (c-ml-string-opener-around-point)) + (goto-char (cadr opener)) + (setq open-dlist (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2)))) + (re-search-forward (funcall c-make-ml-string-closer-re-function + (match-string-no-properties 1)) + nil t)) + (goto-char here) + (cons open-dlist close-dlist)) + + (t (goto-char here) + nil)))) + +(defun c-position-wrt-ml-delims (ml-string-delims) + ;; Given ML-STRING-DELIMS, a structure produced by + ;; `c-ml-string-delims-around-point' called at point, return one of the + ;; following indicating where POINT is with respect to the multi-line + ;; string: + ;; o - nil; not in the string. + ;; o - open-delim: in the open-delimiter. + ;; o - close-delim: in the close-delimiter. + ;; o - after-close: just after the close-delimiter + ;; o - string: inside the delimited string. + (cond + ((null ml-string-delims) + nil) + ((< (point) (cadar ml-string-delims)) + 'open-delim) + ((or (null (cdr ml-string-delims)) + (<= (point) (cadr ml-string-delims))) + 'string) + ((eq (point) (caddr ml-string-delims)) + 'after-close) + (t 'close-delim))) + +(defun c-before-change-check-ml-strings (beg end) + ;; This function clears `syntax-table' text properties from multi-line + ;; strings whose delimiters are about to change in the region (c-new-BEG + ;; c-new-END). BEG and END are the standard arguments supplied to any + ;; before-change function. ;; ;; Point is undefined on both entry and exit, and the return value has no ;; significance. ;; ;; This function is called as a before-change function solely due to its - ;; membership of the C++ value of `c-get-state-before-change-functions'. + ;; membership of mode-specific value of + ;; `c-get-state-before-change-functions'. (goto-char end) - (setq c-raw-string-end-delim-disrupted nil) + (setq c-ml-string-end-delim-disrupted nil) ;; We use the following to detect a R"<id>( being swallowed into a string by ;; the pending change. (setq c-old-END-literality (c-in-literal)) + (goto-char beg) + (setq c-old-beg-ml (c-ml-string-delims-around-point)) + (setq c-beg-pos (c-position-wrt-ml-delims c-old-beg-ml)) + (setq c-old-1-beg-ml + (and (not (or c-old-beg-ml (bobp))) + (goto-char (1- beg)) + (c-ml-string-delims-around-point))) + (goto-char end) + (setq c-old-end-ml + (if (or (eq end beg) + (and c-old-beg-ml + (>= end (caar c-old-beg-ml)) + (or (null (cdr c-old-beg-ml)) + (< end (caddr c-old-beg-ml))))) + c-old-beg-ml + (c-ml-string-delims-around-point))) + (setq c-end-pos (c-position-wrt-ml-delims c-old-end-ml)) + (c-save-buffer-state - ((term-del (c-raw-string-in-end-delim beg end)) + ((term-del (c-ml-string-in-end-delim beg end (car c-old-beg-ml))) Rquote close-quote) - (setq c-old-beg-rs (progn (goto-char beg) (c-raw-string-pos)) - c-old-end-rs (progn (goto-char end) (c-raw-string-pos))) (cond - ;; We're not changing, or we're obliterating raw strings. - ((and (null c-old-beg-rs) (null c-old-end-rs))) - ;; We're changing the putative terminating delimiter of a raw string + ;; We're not changing, or we're obliterating ml strings. + ((and (null c-beg-pos) (null c-end-pos))) + ;; We're changing the putative terminating delimiter of an ml string ;; containing BEG. - ((and c-old-beg-rs term-del - (or (null (nth 3 c-old-beg-rs)) - (<= (car term-del) (nth 3 c-old-beg-rs)))) - (setq Rquote (1- (cadr c-old-beg-rs)) - close-quote (1+ (cdr term-del))) - (setq c-raw-string-end-delim-disrupted t) - (c-depropertize-raw-strings-in-region Rquote close-quote) + ((and c-beg-pos term-del + (or (null (cdr c-old-beg-ml)) + (<= (car term-del) (cadr c-old-beg-ml)))) + (setq Rquote (caar c-old-beg-ml) + close-quote (cdr term-del)) + (setq c-ml-string-end-delim-disrupted t) + (c-depropertize-ml-strings-in-region Rquote close-quote) (setq c-new-BEG (min c-new-BEG Rquote) c-new-END (max c-new-END close-quote))) ;; We're breaking an escaped NL in a raw string in a macro. - ((and c-old-end-rs + ((and c-old-end-ml (< beg end) (goto-char end) (eq (char-before) ?\\) (c-beginning-of-macro)) (let ((bom (point)) (eom (progn (c-end-of-macro) (point)))) - (c-depropertize-raw-strings-in-region bom eom) + (c-depropertize-ml-strings-in-region bom eom) (setq c-new-BEG (min c-new-BEG bom) c-new-END (max c-new-END eom)))) ;; We're changing only the contents of a raw string. - ((and (equal (cdr c-old-beg-rs) (cdr c-old-end-rs)) - (null (car c-old-beg-rs)) (null (car c-old-end-rs)))) + ;; Any critical deletion of "s will be handled in + ;; `c-after-change-unmark-ml-strings'. + ((and (equal c-old-beg-ml c-old-end-ml) + (eq c-beg-pos 'string) (eq c-end-pos 'string))) ((or ;; We're removing (at least part of) the R" of the starting delim of a ;; raw string: - (null c-old-beg-rs) - (and (eq beg (cadr c-old-beg-rs)) + (null c-old-beg-ml) + (and (eq beg (caar c-old-beg-ml)) (< beg end)) ;; Or we're removing the ( of the starting delim of a raw string. - (and (eq (car c-old-beg-rs) 'open-delim) - (or (null c-old-end-rs) - (not (eq (car c-old-end-rs) 'open-delim)) - (not (equal (cdr c-old-beg-rs) (cdr c-old-end-rs)))))) - (let ((close (nth 4 (or c-old-end-rs c-old-beg-rs)))) - (setq Rquote (1- (cadr (or c-old-end-rs c-old-beg-rs))) - close-quote (if close (1+ close) (point-max)))) - (c-depropertize-raw-strings-in-region Rquote close-quote) + (and (eq c-beg-pos 'open-delim) + (or (null c-old-end-ml) + (not (eq c-end-pos 'open-delim)) + (not (equal c-old-beg-ml c-old-end-ml)))) + ;; Or we're disrupting a starting delim by typing into it, or removing + ;; characters from it. + (and (eq c-beg-pos 'open-delim) + (eq c-end-pos 'open-delim) + (equal c-old-beg-ml c-old-end-ml))) + (let ((close (caddr (or c-old-end-ml c-old-beg-ml)))) + (setq Rquote (caar (or c-old-end-ml c-old-beg-ml)) + close-quote (or close (point-max)))) + (c-depropertize-ml-strings-in-region Rquote close-quote) (setq c-new-BEG (min c-new-BEG Rquote) - c-new-END (max c-new-END close-quote))) - ;; We're changing only the text of the identifier of the opening - ;; delimiter of a raw string. - ((and (eq (car c-old-beg-rs) 'open-delim) - (equal c-old-beg-rs c-old-end-rs)))))) - -(defun c-propertize-raw-string-id (start end) - ;; If the raw string identifier between buffer positions START and END - ;; contains any double quote characters, put a punctuation syntax-table text - ;; property on them. The return value is of no significance. - (save-excursion - (goto-char start) - (while (and (skip-chars-forward "^\"" end) - (< (point) end)) - (c-put-char-property (point) 'syntax-table '(1)) - (c-truncate-lit-pos-cache (point)) - (forward-char)))) + c-new-END (max c-new-END close-quote)))))) -(defun c-propertize-raw-string-opener (id open-quote open-paren bound) - ;; Point is immediately after a raw string opening delimiter. Apply any - ;; pertinent `syntax-table' text properties to the delimiter and also the - ;; raw string, should there be a valid matching closing delimiter. - ;; - ;; ID, a string, is the delimiter's identifier. OPEN-QUOTE and OPEN-PAREN - ;; are the buffer positions of the delimiter's components. BOUND is the - ;; bound for searching for a matching closing delimiter; it is usually nil, - ;; but if we're inside a macro, it's the end of the macro (i.e. the position - ;; of the closing newline). - ;; - ;; Point is moved to after the (terminated) raw string and t is returned, or - ;; it is left after the unmatched opening delimiter and nil is returned. - (c-propertize-raw-string-id (1+ open-quote) open-paren) - (prog1 - (if (search-forward (concat ")" id "\"") bound t) - (let ((end-string (match-beginning 0)) - (after-quote (match-end 0))) - (c-propertize-raw-string-id - (1+ (match-beginning 0)) (1- (match-end 0))) - (goto-char open-paren) - (while (progn (skip-syntax-forward "^\"" end-string) - (< (point) end-string)) - (c-put-char-property (point) 'syntax-table '(1)) ; punctuation - (c-truncate-lit-pos-cache (point)) - (forward-char)) - (goto-char after-quote) - t) - (c-put-char-property open-quote 'syntax-table '(1)) ; punctuation - (c-truncate-lit-pos-cache open-quote) - (c-put-char-property open-paren 'syntax-table '(15)) ; generic string - (when bound - ;; In a CPP construct, we try to apply a generic-string - ;; `syntax-table' text property to the last possible character in - ;; the string, so that only characters within the macro get - ;; "stringed out". - (goto-char bound) - (if (save-restriction - (narrow-to-region (1+ open-paren) (point-max)) - (re-search-backward - (eval-when-compile - ;; This regular expression matches either an escape pair - ;; (which isn't an escaped NL) (submatch 5) or a - ;; non-escaped character (which isn't itself a backslash) - ;; (submatch 10). The long preambles to these - ;; (respectively submatches 2-4 and 6-9) ensure that we - ;; have the correct parity for sequences of backslashes, - ;; etc.. - (concat "\\(" ; 1 - "\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)*" ; 2-4 - "\\(\\\\.\\)" ; 5 - "\\|" - "\\(\\`\\|[^\\]\\|\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)+\\)" ; 6-9 - "\\([^\\]\\)" ; 10 - "\\)" - "\\(\\\\\n\\)*\\=")) ; 11 - (1+ open-paren) t)) - (if (match-beginning 10) - (progn - (c-put-char-property (match-beginning 10) 'syntax-table '(15)) - (c-truncate-lit-pos-cache (match-beginning 10))) - (c-put-char-property (match-beginning 5) 'syntax-table '(1)) - (c-put-char-property (1+ (match-beginning 5)) 'syntax-table '(15)) - (c-truncate-lit-pos-cache (1+ (match-beginning 5)))) - ;; (c-put-char-property open-paren 'syntax-table '(1)) - ) - (goto-char bound)) - nil))) - -(defun c-after-change-unmark-raw-strings (beg end _old-len) - ;; This function removes `syntax-table' text properties from any raw strings +(defun c-after-change-unmark-ml-strings (beg end old-len) + ;; This function removes `syntax-table' text properties from any ml strings ;; which have been affected by the current change. These are those which - ;; have been "stringed out" and from newly formed raw strings, or any - ;; existing raw string which the new text terminates. BEG, END, and - ;; _OLD-LEN are the standard arguments supplied to any + ;; have been "stringed out" and from newly formed ml strings, or any + ;; existing ml string which the new text terminates. BEG, END, and + ;; OLD-LEN are the standard arguments supplied to any ;; after-change-function. ;; ;; Point is undefined on both entry and exit, and the return value has no ;; significance. ;; ;; This functions is called as an after-change function by virtue of its - ;; membership of the C++ value of `c-before-font-lock-functions'. + ;; membership of the mode's value of `c-before-font-lock-functions'. ;; (when (< beg end) - (c-save-buffer-state (found eoll state id found-beg) - ;; Has an inserted " swallowed up a R"(, turning it into "...R"(? + ;; + ;; Maintainers' note: Be careful with the use of `c-old-beg-ml' and + ;; `c-old-end-ml'; since text has been inserted or removed, most of the + ;; components in these variables will no longer be valid. (caar + ;; c-old-beg-ml) is normally OK, (cadar c-old-beg-ml) often is, any others + ;; will need adjstments. + (c-save-buffer-state (found eoll state opener) + ;; Has an inserted " swallowed up a R"(, turning it into "...R"(? + (goto-char end) + (setq eoll (c-point 'eoll)) + (when (and (null c-old-END-literality) + (search-forward-regexp c-ml-string-opener-re eoll t)) + (setq state (c-semi-pp-to-literal end)) + (when (eq (cadr state) 'string) + (unwind-protect + ;; Temporarily insert a closing string delimiter.... + (progn + (goto-char end) + (cond + ((c-characterp (nth 3 (car state))) + (insert (nth 3 (car state)))) + ((eq (nth 3 (car state)) t) + (insert ?\") + (c-put-char-property end 'syntax-table '(15)))) + (c-truncate-lit-pos-cache end) + ;; ....ensure c-new-END extends right to the end of the about + ;; to be un-stringed raw string.... + (save-excursion + (goto-char (1+ (match-end 1))) ; Count inserted " too. + (setq c-new-END + (max c-new-END + (if (re-search-forward + (funcall c-make-ml-string-closer-re-function + (match-string-no-properties 1)) + nil t) + (1- (match-end 1)) ; 1- For the inserted ". + eoll)))) + + ;; ...and clear `syntax-table' text propertes from the + ;; following raw strings. + (c-depropertize-ml-strings-in-region (point) (1+ eoll))) + ;; Remove the temporary string delimiter. + (goto-char end) + (delete-char 1) + (c-truncate-lit-pos-cache end)))) + + ;; Have we just created a new starting id? + (goto-char beg) + (setq opener + (if (eq beg end) + (c-ml-string-opener-at-or-around-point end) + (c-ml-string-opener-intersects-region beg end))) + (when + (and opener (<= (car opener) end) + (setq state (c-semi-pp-to-literal (car opener))) + (not (cadr state))) + (setq c-new-BEG (min c-new-BEG (car opener))) + (goto-char (cadr opener)) + (when (re-search-forward + (funcall c-make-ml-string-closer-re-function + (buffer-substring-no-properties + (car opener) (cadr opener))) + nil t) ; No bound + (setq c-new-END (max c-new-END (match-end 1)))) + (goto-char c-new-BEG) + (while (c-search-forward-char-property-with-value-on-char + 'syntax-table '(15) ?\" c-new-END) + (c-remove-string-fences (1- (point)))) + (c-depropertize-ml-strings-in-region c-new-BEG c-new-END)) + + ;; Have we matched up with an existing terminator by typing into or + ;; deleting from an opening delimiter? ... or by messing up a raw string's + ;; terminator so that it now matches a later terminator? + (when + (cond + ((or c-ml-string-end-delim-disrupted + (and c-old-beg-ml + (eq c-beg-pos 'open-delim))) + (goto-char (caar c-old-beg-ml))) + ((and (< beg end) + (not c-old-beg-ml) + c-old-1-beg-ml + (save-excursion + (goto-char (1- beg)) + (c-ml-string-back-to-neutral (caar c-old-1-beg-ml)) + (re-search-forward + (funcall c-make-ml-string-closer-re-function + (buffer-substring-no-properties + (caar c-old-1-beg-ml) + (cadar c-old-1-beg-ml))) + nil 'bound) + (> (point) beg))) + (goto-char (caar c-old-1-beg-ml)) + (setq c-new-BEG (min c-new-BEG (point))) + (c-truncate-lit-pos-cache (point)))) + + (when (looking-at c-ml-string-opener-re) + (goto-char (match-end 1)) + (when (re-search-forward (funcall c-make-ml-string-closer-re-function + (match-string-no-properties 1)) + nil t) ; No bound + ;; If what is to be the new delimiter was previously an unterminated + ;; ordinary string, clear the c-fl-syn-tab properties from this old + ;; string. + (when (c-get-char-property (match-beginning 2) 'c-fl-syn-tab) + (c-remove-string-fences (match-beginning 2))) + (setq c-new-END (point-max)) + (c-clear-char-properties (caar (or c-old-beg-ml c-old-1-beg-ml)) + c-new-END + 'syntax-table) + (c-truncate-lit-pos-cache + (caar (or c-old-beg-ml c-old-1-beg-ml)))))) + + ;; Have we disturbed the innards of an ml string, possibly by deleting "s? + (when (and + c-old-beg-ml + (eq c-beg-pos 'string) + (eq beg end)) + (goto-char beg) + (c-ml-string-back-to-neutral (caar c-old-beg-ml)) + (let ((bound (if (cdr c-old-end-ml) + (min (+ (- (caddr c-old-end-ml) old-len) + c-ml-string-max-closer-len-no-leader) + (point-max)) + (point-max))) + (new-END-end-ml-string + (if (cdr c-old-end-ml) + (- (caddr c-old-end-ml) old-len) + (point-max)))) + (when (and + (re-search-forward + (funcall c-make-ml-string-closer-re-function + (buffer-substring-no-properties + (caar c-old-beg-ml) (cadar c-old-beg-ml))) + bound 'bound) + (< (match-end 1) new-END-end-ml-string)) + (setq c-new-END (max new-END-end-ml-string c-new-END)) + (c-clear-char-properties (caar c-old-beg-ml) c-new-END + 'syntax-table) + (setq c-new-BEG (min (caar c-old-beg-ml) c-new-BEG)) + (c-truncate-lit-pos-cache (caar c-old-beg-ml))))) + + ;; Have we terminated an existing raw string by inserting or removing + ;; text? + (when + (and + (< beg end) + (eq c-old-END-literality 'string) + c-old-beg-ml) + ;; Have we just made or modified a closing delimiter? (goto-char end) - (setq eoll (c-point 'eoll)) - (when (and (null c-old-END-literality) - (search-forward-regexp c-c++-raw-string-opener-re eoll t)) - (setq state (c-semi-pp-to-literal end)) - (when (eq (cadr state) 'string) - (unwind-protect - ;; Temporarily insert a closing string delimiter.... - (progn - (goto-char end) - (cond - ((c-characterp (nth 3 (car state))) - (insert (nth 3 (car state)))) - ((eq (nth 3 (car state)) t) - (insert ?\") - (c-put-char-property end 'syntax-table '(15)))) - (c-truncate-lit-pos-cache end) - ;; ....ensure c-new-END extends right to the end of the about - ;; to be un-stringed raw string.... - (save-excursion - (goto-char (match-beginning 1)) - (let ((end-bs (c-raw-string-pos))) - (setq c-new-END - (max c-new-END - (if (nth 4 end-bs) - (1+ (nth 4 end-bs)) - eoll))))) - - ;; ...and clear `syntax-table' text propertes from the - ;; following raw strings. - (c-depropertize-raw-strings-in-region (point) (1+ eoll))) - ;; Remove the temporary string delimiter. - (goto-char end) - (delete-char 1)))) - - ;; Have we just created a new starting id? - (goto-char (max (- beg 18) (point-min))) + (c-ml-string-back-to-neutral (caar c-old-beg-ml)) (while (and (setq found - (search-forward-regexp c-c++-raw-string-opener-re - c-new-END 'bound)) - (<= (match-end 0) beg))) + (search-forward-regexp + c-ml-string-any-closer-re + (+ (c-point 'eol end) + (1- c-ml-string-max-closer-len-no-leader)) + t)) + (< (match-end 1) beg)) + (goto-char (match-end 1))) (when (and found (<= (match-beginning 0) end)) - (setq c-new-BEG (min c-new-BEG (match-beginning 0))) - (c-depropertize-raw-strings-in-region c-new-BEG c-new-END)) - - ;; Have we invalidated an opening delimiter by typing into it? - (when (and c-old-beg-rs - (eq (car c-old-beg-rs) 'open-delim) - (equal (c-get-char-property (cadr c-old-beg-rs) - 'syntax-table) - '(1))) - (goto-char (1- (cadr c-old-beg-rs))) - (unless (looking-at c-c++-raw-string-opener-re) - (c-clear-char-property (1+ (point)) 'syntax-table) - (c-truncate-lit-pos-cache (1+ (point))) - (if (c-search-forward-char-property 'syntax-table '(15) - (c-point 'eol)) - (c-clear-char-property (1- (point)) 'syntax-table)))) - - ;; Have we matched up with an existing terminator by typing into an - ;; opening delimiter? ... or by messing up a raw string's terminator so - ;; that it now matches a later terminator? - (when - (or c-raw-string-end-delim-disrupted - (and c-old-beg-rs - (eq (car c-old-beg-rs) 'open-delim))) - (goto-char (cadr c-old-beg-rs)) - (when (looking-at c-c++-raw-string-opener-1-re) - (setq id (match-string-no-properties 1)) - (when (search-forward (concat ")" id "\"") nil t) ; No bound. - (setq c-new-END (point-max)) - (c-clear-char-properties (cadr c-old-beg-rs) c-new-END - 'syntax-table) - (c-truncate-lit-pos-cache (cadr c-old-beg-rs))))) - ;; Have we terminated an existing raw string by inserting or removing - ;; text? - (when (eq c-old-END-literality 'string) - ;; Have we just made or modified a closing delimiter? - (goto-char (max (- beg 18) (point-min))) - (while - (and - (setq found - (search-forward-regexp ")\\([^ ()\\\n\r\t]\\{0,16\\}\\)\"" - (+ end 17) t)) - (< (match-end 0) beg))) - (when (and found (<= (match-beginning 0) end)) - (setq id (match-string-no-properties 1)) - (goto-char (match-beginning 0)) + (let ((opener-re (funcall c-make-ml-string-opener-re-function + (match-string 1)))) (while (and - (setq found (search-backward (concat "R\"" id "(") nil t)) + (setq found (re-search-backward opener-re nil t)) (setq state (c-semi-pp-to-literal (point))) - (memq (nth 3 (car state)) '(t ?\")))) - (when found - (setq c-new-BEG (min (point) c-new-BEG) - c-new-END (point-max)) - (c-clear-syn-tab-properties (point) c-new-END) - (c-truncate-lit-pos-cache (point))))) - - ;; Are there any raw strings in a newly created macro? - (when (< beg end) - (goto-char beg) - (setq found-beg (point)) - (when (search-forward-regexp c-anchored-cpp-prefix end t) + (memq (nth 3 (car state)) '(t ?\"))))) + (when found + (setq c-new-BEG (min (point) c-new-BEG) + c-new-END (point-max)) + (c-clear-syn-tab-properties (point) c-new-END) + (c-truncate-lit-pos-cache (point))))) + + ;; Are there any raw strings in a newly created macro? + (goto-char (c-point 'bol beg)) + (while (and (< (point) (c-point 'eol end)) + (re-search-forward c-anchored-cpp-prefix (c-point 'eol end) + 'boundt)) + (when (and (<= beg (match-end 1)) + (>= end (match-beginning 1))) + (goto-char (match-beginning 1)) (c-end-of-macro) - (c-depropertize-raw-strings-in-region found-beg (point)))))) + (c-depropertize-ml-strings-in-region + (match-beginning 1) (point)))))) -(defun c-maybe-re-mark-raw-string () +(defun c-maybe-re-mark-ml-string () ;; When this function is called, point is immediately after a " which opens - ;; a string. If this " is the characteristic " of a raw string - ;; opener, apply the pertinent `syntax-table' text properties to the - ;; entire raw string (when properly terminated) or just the delimiter - ;; (otherwise). In either of these cases, return t, otherwise return nil. - ;; - (let (in-macro macro-end) + ;; a string. If this " is the characteristic " of a multi-line string + ;; opener, apply the pertinent `syntax-table' text properties to the entire + ;; ml string (when properly terminated) or just the delimiter (otherwise). + ;; In either of these cases, return t, otherwise return nil. Point is moved + ;; to after the terminated raw string, or to the end of the containing + ;; macro, or to point-max. + ;; + (let (delim in-macro macro-end) (when (and - (eq (char-before (1- (point))) ?R) - (looking-at "\\([^ ()\\\n\r\t]\\{0,16\\}\\)(")) + (setq delim (c-ml-string-opener-at-or-around-point (1- (point)))) + (save-excursion + (goto-char (car delim)) + (not (c-in-literal)))) (save-excursion (setq in-macro (c-beginning-of-macro)) (setq macro-end (when in-macro (c-end-of-macro) - (point) ;; (min (1+ (point)) (point-max)) + (point) ))) (when (not - (c-propertize-raw-string-opener - (match-string-no-properties 1) ; id - (1- (point)) ; open quote - (match-end 1) ; open paren - macro-end)) ; bound (end of macro) or nil. + (c-propertize-ml-string-opener + delim + macro-end)) ; bound (end of macro) or nil. (goto-char (or macro-end (point-max)))) t))) +(defun c-propertize-ml-string-id (delim) + ;; Apply punctuation ('(1)) syntax-table text properties to the opening or + ;; closing delimiter given by the three element dotted list DELIM, such that + ;; its "total syntactic effect" is that of a single ". + (save-excursion + (goto-char (car delim)) + (while (and (skip-chars-forward c-ml-string-non-punc-skip-chars + (cadr delim)) + (< (point) (cadr delim))) + (when (not (eq (point) (cddr delim))) + (c-put-char-property (point) 'syntax-table '(1)) + (c-truncate-lit-pos-cache (point))) + (forward-char)))) + +(defun c-propertize-ml-string-opener (delim bound) + ;; DELIM defines the opening delimiter of a multi-line string in the + ;; way returned by `c-ml-string-opener-around-point'. Apply any + ;; pertinent `syntax-table' text properties to this opening delimiter and in + ;; the case of a terminated ml string, also to the innards of the string and + ;; the terminating delimiter. + ;; + ;; BOUND is the end of the macro we're inside (i.e. the position of the + ;; closing newline), if any, otherwise nil. + ;; + ;; Point is undefined at the function start. For a terminated ml string, + ;; point is left after the terminating delimiter and t is returned. For an + ;; unterminated string, point is left at the end of the macro, if any, or + ;; after the unmatched opening delimiter, and nil is returned. + (c-propertize-ml-string-id delim) + (goto-char (cadr delim)) + (if (re-search-forward + (funcall c-make-ml-string-closer-re-function + (buffer-substring-no-properties + (car delim) (cadr delim))) + bound t) + + (let ((end-delim + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2))))) + (c-propertize-ml-string-id end-delim) + (goto-char (cadr delim)) + (while (progn (skip-syntax-forward c-ml-string-non-punc-skip-chars + (car end-delim)) + (< (point) (car end-delim))) + (c-put-char-property (point) 'syntax-table '(1)) ; punctuation + (c-truncate-lit-pos-cache (point)) + (forward-char)) + (goto-char (cadr end-delim)) + t) + (c-put-char-property (cddr delim) 'syntax-table '(1)) + (c-put-char-property (1- (cadr delim)) 'syntax-table '(15)) + (c-truncate-lit-pos-cache (1- (cddr delim))) + (when bound + ;; In a CPP construct, we try to apply a generic-string + ;; `syntax-table' text property to the last possible character in + ;; the string, so that only characters within the macro get + ;; "stringed out". + (goto-char bound) + (if (save-restriction + (narrow-to-region (cadr delim) (point-max)) + (re-search-backward + (eval-when-compile + ;; This regular expression matches either an escape pair + ;; (which isn't an escaped NL) (submatch 5) or a + ;; non-escaped character (which isn't itself a backslash) + ;; (submatch 10). The long preambles to these + ;; (respectively submatches 2-4 and 6-9) ensure that we + ;; have the correct parity for sequences of backslashes, + ;; etc.. + (concat "\\(" ; 1 + "\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)*" ; 2-4 + "\\(\\\\.\\)" ; 5 + "\\|" + "\\(\\`\\|[^\\]\\|\\(\\`[^\\]?\\|[^\\][^\\]\\)\\(\\\\\\(.\\|\n\\)\\)+\\)" ; 6-9 + "\\([^\\]\\)" ; 10 + "\\)" + "\\(\\\\\n\\)*\\=")) ; 11 + (cadr delim) t)) + (if (match-beginning 10) + (progn + (c-put-char-property (match-beginning 10) 'syntax-table '(15)) + (c-truncate-lit-pos-cache (match-beginning 10))) + (c-put-char-property (match-beginning 5) 'syntax-table '(1)) + (c-put-char-property (1+ (match-beginning 5)) 'syntax-table '(15)) + (c-truncate-lit-pos-cache (match-beginning 5)))) + (goto-char bound)) + nil)) + +(defvar c-neutralize-pos nil) + ;; Buffer position of character neutralized by punctuation syntax-table + ;; text property ('(1)), or nil if there's no such character. +(defvar c-neutralized-prop nil) + ;; syntax-table text property that was on the character at + ;; `c-neutralize-pos' before it was replaced with '(1), or nil if none. + +(defun c-depropertize-ml-string (string-delims bound) + ;; Remove any `syntax-table' text properties associated with the opening + ;; delimiter of a multi-line string (if it's unmatched) or with the entire + ;; string. Exception: A single punctuation ('(1)) property will be left on + ;; a string character to make the entire set of multi-line strings + ;; syntactically neutral. This is done using the global variable + ;; `c-neutralize-pos', the position of this property (or nil if there is + ;; none). + ;; + ;; STRING-DELIMS, of the form of the output from + ;; `c-ml-string-delims-around-point' defines the current ml string. BOUND + ;; is the bound for searching for a matching closing delimiter; it is + ;; usually nil, but if we're inside a macro, it's the end of the macro + ;; (i.e. just before the terminating \n). + ;; + ;; Point is undefined on input, and is moved to after the (terminated) raw + ;; string, or left after the unmatched opening delimiter, as the case may + ;; be. The return value is of no significance. + + ;; Handle the special case of a closing " previously having been an + ;; unterminated ordinary string. + (when + (and + (cdr string-delims) + (equal (c-get-char-property (cdddr string-delims) ; pos of closing ". + 'syntax-table) + '(15))) + (goto-char (cdddr string-delims)) + (when (c-safe (c-forward-sexp)) ; To '(15) at EOL. + (c-clear-char-property (1- (point)) 'syntax-table) + (c-truncate-lit-pos-cache (1- (point))))) + ;; The '(15) in the closing delimiter will be cleared by the following. + + (c-depropertize-ml-string-delims string-delims) + (let ((bound1 (if (cdr string-delims) + (caddr string-delims) ; end of closing delimiter. + bound)) + first s) + (if (and + bound1 + (setq first (c-clear-char-properties (cadar string-delims) bound1 + 'syntax-table))) + (c-truncate-lit-pos-cache first)) + (setq s (parse-partial-sexp (or c-neutralize-pos (caar string-delims)) + (or bound1 (point-max)))) + (cond + ((not (nth 3 s))) ; Nothing changed by this ml-string. + ((not c-neutralize-pos) ; "New" unbalanced quote in this ml-s. + (setq c-neutralize-pos (nth 8 s)) + (setq c-neutralized-prop (c-get-char-property c-neutralize-pos + 'syntax-table)) + (c-put-char-property c-neutralize-pos 'syntax-table '(1)) + (c-truncate-lit-pos-cache c-neutralize-pos)) + ((eq (nth 3 s) (char-after c-neutralize-pos)) + ;; New unbalanced quote balances old one. + (if c-neutralized-prop + (c-put-char-property c-neutralize-pos 'syntax-table + c-neutralized-prop) + (c-clear-char-property c-neutralize-pos 'syntax-table)) + (c-truncate-lit-pos-cache c-neutralize-pos) + (setq c-neutralize-pos nil)) + ;; New unbalanced quote doesn't balance old one. Nothing to do. + ))) + +(defun c-depropertize-ml-strings-in-region (start finish) + ;; Remove any `syntax-table' text properties associated with multi-line + ;; strings contained in the region (START FINISH). Point is undefined at + ;; entry and exit, and the return value has no significance. + (setq c-neutralize-pos nil) + (goto-char start) + (while (and (< (point) finish) + (re-search-forward + c-ml-string-cpp-or-opener-re + finish t)) + (if (match-beginning (+ c-cpp-or-ml-match-offset 1)) ; opening delimiter + ;; We've found a raw string + (let ((open-delim + (cons (match-beginning (+ c-cpp-or-ml-match-offset 1)) + (cons (match-end (+ c-cpp-or-ml-match-offset 1)) + (match-beginning (+ c-cpp-or-ml-match-offset 2)))))) + (c-depropertize-ml-string + (cons open-delim + (when + (and + (re-search-forward + (funcall c-make-ml-string-closer-re-function + (match-string-no-properties + (+ c-cpp-or-ml-match-offset 1))) + (min (+ finish c-ml-string-max-closer-len-no-leader) + (point-max)) + t) + (<= (match-end 1) finish)) + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2))))) + nil)) ; bound + ;; We've found a CPP construct. Search for raw strings within it. + (goto-char (match-beginning 2)) ; the "#" + (c-end-of-macro) + (let ((eom (point))) + (goto-char (match-end 2)) ; after the "#". + (while (and (< (point) eom) + (c-syntactic-re-search-forward + c-ml-string-opener-re eom t)) + (save-excursion + (let ((open-delim (cons (match-beginning 1) + (cons (match-end 1) + (match-beginning 2))))) + (c-depropertize-ml-string + (cons open-delim + (when (re-search-forward + (funcall c-make-ml-string-closer-re-function + (match-string-no-properties 1)) + eom t) + (cons (match-beginning 1) + (cons (match-end 1) (match-beginning 2))))) + eom))))))) ; bound. + (when c-neutralize-pos + (if c-neutralized-prop + (c-put-char-property c-neutralize-pos 'syntax-table + c-neutralized-prop) + (c-clear-char-property c-neutralize-pos 'syntax-table)) + (c-truncate-lit-pos-cache c-neutralize-pos))) + ;; Handling of small scale constructs like types and names. @@ -7771,6 +8189,7 @@ comment at the start of cc-engine.el for more info." (defvar c-last-identifier-range nil) (defmacro c-record-type-id (range) + (declare (debug t)) (if (eq (car-safe range) 'cons) ;; Always true. `(setq c-record-type-identifiers @@ -7781,6 +8200,7 @@ comment at the start of cc-engine.el for more info." (cons range c-record-type-identifiers)))))) (defmacro c-record-ref-id (range) + (declare (debug t)) (if (eq (car-safe range) 'cons) ;; Always true. `(setq c-record-ref-identifiers @@ -7806,6 +8226,7 @@ comment at the start of cc-engine.el for more info." ;; if TYPE is 'type or as a reference if TYPE is 'ref. ;; ;; This macro might do hidden buffer changes. + (declare (debug t)) `(let (res) (setq c-last-identifier-range nil) (while (if (setq res ,(if (eq type 'type) @@ -7830,6 +8251,7 @@ comment at the start of cc-engine.el for more info." ;; `c-forward-keyword-prefixed-id'. ;; ;; This macro might do hidden buffer changes. + (declare (debug t)) `(while (and (progn ,(when update-safe-pos '(setq safe-pos (point))) @@ -8042,13 +8464,14 @@ comment at the start of cc-engine.el for more info." ;; bracket arglist. It's propagated through the return value ;; on successful completion. (c-record-found-types c-record-found-types) + (syntax-table-prop-on-< (c-get-char-property (point) 'syntax-table)) ;; List that collects the positions after the argument ;; separating ',' in the arglist. arg-start-pos) ;; If the '<' has paren open syntax then we've marked it as an angle ;; bracket arglist before, so skip to the end. (if (and (not c-parse-and-markup-<>-arglists) - (c-get-char-property (point) 'syntax-table)) + syntax-table-prop-on-<) (progn (forward-char) @@ -8133,8 +8556,20 @@ comment at the start of cc-engine.el for more info." (c-put-c-type-property (1- (car arg-start-pos)) 'c-<>-arg-sep) (setq arg-start-pos (cdr arg-start-pos))) + (when (and (not syntax-table-prop-on-<) + (c-get-char-property (1- (point)) + 'syntax-table)) + ;; Clear the now spuriously matching < of its + ;; syntax-table property. This could happen on + ;; inserting "_cast" into "static <" with C-y. + (save-excursion + (and (c-go-list-backward) + (eq (char-after) ?<) + (c-truncate-lit-pos-cache (point)) + (c-unmark-<->-as-paren (point))))) (c-mark-<-as-paren start) - (c-mark->-as-paren (1- (point)))) + (c-mark->-as-paren (1- (point))) + (c-truncate-lit-pos-cache start)) (setq res t) nil)) ; Exit the loop. @@ -8298,7 +8733,7 @@ comment at the start of cc-engine.el for more info." ;; o - nil if no name is found; ;; o - 'template if it's an identifier ending with an angle bracket ;; arglist; - ;; o - 'operator of it's an operator identifier; + ;; o - 'operator if it's an operator identifier; ;; o - t if it's some other kind of name. ;; ;; This function records identifier ranges on @@ -8318,6 +8753,7 @@ comment at the start of cc-engine.el for more info." (lim+ (c-determine-+ve-limit 500))) (while (and + (< (point) lim+) (looking-at c-identifier-key) (progn @@ -8367,23 +8803,28 @@ comment at the start of cc-engine.el for more info." ;; '*', '&' or a name followed by ":: *", ;; where each can be followed by a sequence ;; of `c-opt-type-modifier-key'. - (while (cond ((looking-at "[*&]") - (goto-char (match-end 0)) - t) - ((looking-at c-identifier-start) - (and (c-forward-name) - (looking-at "::") - (progn - (goto-char (match-end 0)) - (c-forward-syntactic-ws lim+) - (eq (char-after) ?*)) - (progn - (forward-char) - t)))) + (while + (and + (< (point) lim+) + (cond ((looking-at "[*&]") + (goto-char (match-end 0)) + t) + ((looking-at c-identifier-start) + (and (c-forward-name) + (looking-at "::") + (progn + (goto-char (match-end 0)) + (c-forward-syntactic-ws lim+) + (eq (char-after) ?*)) + (progn + (forward-char) + t))))) (while (progn (c-forward-syntactic-ws lim+) (setq pos (point)) - (looking-at c-opt-type-modifier-key)) + (and + (<= (point) lim+) + (looking-at c-opt-type-modifier-key))) (goto-char (match-end 1)))))) ((looking-at c-overloadable-operators-regexp) @@ -8429,6 +8870,9 @@ comment at the start of cc-engine.el for more info." ;; Maybe an angle bracket arglist. (when (let (c-last-identifier-range) (c-forward-<>-arglist nil)) + ;; <> arglists can legitimately be very long, so recalculate + ;; `lim+'. + (setq lim+ (c-determine-+ve-limit 500)) (c-forward-syntactic-ws lim+) (unless (eq (char-after) ?\() @@ -8764,6 +9208,7 @@ comment at the start of cc-engine.el for more info." (defmacro c-pull-open-brace (ps) ;; Pull the next open brace from PS (which has the form of paren-state), ;; skipping over any brace pairs. Returns NIL when PS is exhausted. + (declare (debug (symbolp))) `(progn (while (consp (car ,ps)) (setq ,ps (cdr ,ps))) @@ -8879,6 +9324,7 @@ comment at the start of cc-engine.el for more info." ;; a comma. If either of <symbol> or bracketed <expression> is missing, ;; throw nil to 'level. If the terminating } or ) is unmatched, throw nil ;; to 'done. This is not a general purpose macro! + (declare (debug t)) `(while (eq (char-before) ?,) (backward-char) (c-backward-syntactic-ws ,limit) @@ -9272,6 +9718,7 @@ This function might do hidden buffer changes." ;; sometimes consumes the identifier in the declaration as a type. ;; This is used to "backtrack" and make the last type be treated as ;; an identifier instead. + (declare (debug nil)) `(progn ,(unless short ;; These identifiers are bound only in the inner let. @@ -11419,7 +11866,9 @@ comment at the start of cc-engine.el for more info." ;; also might be part of a declarator expression. Currently ;; there's no such language. (not (or (looking-at c-symbol-start) - (looking-at c-type-decl-prefix-key)))))) + (looking-at c-type-decl-prefix-key) + (and (eq (char-after) ?{) + (not (c-looking-at-statement-block)))))))) ;; In Pike a list of modifiers may be followed by a brace ;; to make them apply to many identifiers. Note that the @@ -11826,15 +12275,17 @@ comment at the start of cc-engine.el for more info." ;; POINT, or nil if there is no such position, or we do not know it. LIM is ;; a backward search limit. ;; - ;; The determination of whether the brace starts a brace list is solely by - ;; the context of the brace, not by its contents. + ;; The determination of whether the brace starts a brace list is mainly by + ;; the context of the brace, not by its contents. In exceptional + ;; circumstances (e.g. "struct A {" in C++ Mode), the contents are examined, + ;; too. ;; ;; Here, "brace list" does not include the body of an enum. (save-excursion (let ((start (point)) (braceassignp 'dontknow) inexpr-brace-list bufpos macro-start res pos after-type-id-pos - in-paren parens-before-brace + pos2 in-paren parens-before-brace paren-state paren-pos) (setq res (c-backward-token-2 1 t lim)) @@ -11850,12 +12301,16 @@ comment at the start of cc-engine.el for more info." (goto-char paren-pos) (setq braceassignp 'c++-noassign in-paren 'in-paren)) - ((looking-at c-pre-id-bracelist-key) + ((looking-at c-pre-brace-non-bracelist-key) (setq braceassignp nil)) ((looking-at c-return-key)) ((and (looking-at c-symbol-start) (not (looking-at c-keywords-regexp))) - (setq after-type-id-pos (point))) + (if (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))) + (setq braceassignp 'c++-noassign) + (setq after-type-id-pos (point)))) ((eq (char-after) ?\() (setq parens-before-brace t) nil) @@ -11869,8 +12324,13 @@ comment at the start of cc-engine.el for more info." (eq (char-after paren-pos) ?\() (setq in-paren 'in-paren) (goto-char paren-pos))) - ((looking-at c-pre-id-bracelist-key)) + ((looking-at c-pre-brace-non-bracelist-key)) ((looking-at c-return-key)) + ((and (looking-at c-symbol-start) + (not (looking-at c-keywords-regexp)) + (save-excursion + (and (zerop (c-backward-token-2 1 t lim)) + (looking-at c-pre-id-bracelist-key))))) (t (setq after-type-id-pos (point)) nil)))) (setq braceassignp 'c++-noassign)) @@ -11955,8 +12415,12 @@ comment at the start of cc-engine.el for more info." (cond (braceassignp ;; We've hit the beginning of the aggregate list. - (c-beginning-of-statement-1 containing-sexp) - (cons (point) (or in-paren inexpr-brace-list))) + (setq pos2 (point)) + (cons + (if (eq (c-beginning-of-statement-1 containing-sexp) 'same) + (point) + pos2) + (or in-paren inexpr-brace-list))) ((and after-type-id-pos (save-excursion (when (eq (char-after) ?\;) @@ -11968,34 +12432,36 @@ comment at the start of cc-engine.el for more info." (c-get-char-property (point) 'syntax-table)) (c-go-list-forward nil after-type-id-pos) (c-forward-syntactic-ws))) - (and - (or (not (looking-at c-class-key)) - (save-excursion - (goto-char (match-end 1)) - (c-forward-syntactic-ws) - (not (eq (point) after-type-id-pos)))) - (progn - (setq res - (c-forward-decl-or-cast-1 - (save-excursion (c-backward-syntactic-ws) (point)) - nil nil)) - (and (consp res) - (cond - ((eq (car res) after-type-id-pos)) - ((> (car res) after-type-id-pos) nil) - (t - (catch 'find-decl - (save-excursion - (goto-char (car res)) - (c-do-declarators - (point-max) t nil nil - (lambda (id-start _id-end _tok _not-top _func _init) - (cond - ((> id-start after-type-id-pos) - (throw 'find-decl nil)) - ((eq id-start after-type-id-pos) - (throw 'find-decl t))))) - nil))))))))) + (if (and (not (eq (point) after-type-id-pos)) + (or (not (looking-at c-class-key)) + (save-excursion + (goto-char (match-end 1)) + (c-forward-syntactic-ws) + (not (eq (point) after-type-id-pos))))) + (progn + (setq res + (c-forward-decl-or-cast-1 (c-point 'bosws) + nil nil)) + (and (consp res) + (cond + ((eq (car res) after-type-id-pos)) + ((> (car res) after-type-id-pos) nil) + (t + (catch 'find-decl + (save-excursion + (goto-char (car res)) + (c-do-declarators + (point-max) t nil nil + (lambda (id-start _id-end _tok _not-top _func _init) + (cond + ((> id-start after-type-id-pos) + (throw 'find-decl nil)) + ((eq id-start after-type-id-pos) + (throw 'find-decl t))))) + nil)))))) + (save-excursion + (goto-char start) + (not (c-looking-at-statement-block)))))) (cons bufpos (or in-paren inexpr-brace-list))) ((or (eq (char-after) ?\;) ;; Brace lists can't contain a semicolon, so we're done. @@ -12145,33 +12611,31 @@ comment at the start of cc-engine.el for more info." (defun c-looking-at-statement-block () ;; Point is at an opening brace. If this is a statement block (i.e. the ;; elements in the block are terminated by semicolons, or the block is - ;; empty, or the block contains a keyword) return non-nil. Otherwise, - ;; return nil. + ;; empty, or the block contains a characteristic keyword, or there is a + ;; nested statement block) return non-nil. Otherwise, return nil. (let ((here (point))) (prog1 (if (c-go-list-forward) (let ((there (point))) (backward-char) - (c-syntactic-skip-backward "^;," here t) + (c-syntactic-skip-backward "^;" here t) (cond - ((eq (char-before) ?\;) t) - ((eq (char-before) ?,) nil) - (t ; We're at (1+ here). - (cond - ((progn (c-forward-syntactic-ws) - (eq (point) (1- there)))) - ((c-syntactic-re-search-forward c-keywords-regexp there t)) - ((c-syntactic-re-search-forward "{" there t t) - (backward-char) - (c-looking-at-statement-block)) - (t nil))))) + ((eq (char-before) ?\;)) + ((progn (c-forward-syntactic-ws) + (eq (point) (1- there)))) + ((c-syntactic-re-search-forward + c-stmt-block-only-keywords-regexp there t)) + ((c-syntactic-re-search-forward "{" there t t) + (backward-char) + (c-looking-at-statement-block)) + (t nil))) (forward-char) (cond - ((c-syntactic-re-search-forward "[;,]" nil t t) - (eq (char-before) ?\;)) + ((c-syntactic-re-search-forward ";" nil t t)) ((progn (c-forward-syntactic-ws) (eobp))) - ((c-syntactic-re-search-forward c-keywords-regexp nil t t)) + ((c-syntactic-re-search-forward c-stmt-block-only-keywords-regexp + nil t t)) ((c-syntactic-re-search-forward "{" nil t t) (backward-char) (c-looking-at-statement-block)) @@ -12211,7 +12675,7 @@ comment at the start of cc-engine.el for more info." (save-excursion (while (progn - (c-syntactic-skip-backward "^;=}>" closest-lim t) + (c-syntactic-skip-backward "^;=,}>" closest-lim t) (and (eq (char-before) ?>) (c-backward-token-2) (not (looking-at c-haskell-op-re))))) @@ -14658,18 +15122,6 @@ Cannot combine absolute offsets %S and %S in `add' method" indent))) -(def-edebug-spec c-bos-pop-state t) -(def-edebug-spec c-bos-save-error-info t) -(def-edebug-spec c-state-cache-top-lparen t) -(def-edebug-spec c-state-cache-top-paren t) -(def-edebug-spec c-state-cache-after-top-paren t) -(def-edebug-spec c-state-maybe-marker (form symbolp)) -(def-edebug-spec c-record-type-id t) -(def-edebug-spec c-record-ref-id t) -(def-edebug-spec c-forward-keyword-prefixed-id t) -(def-edebug-spec c-forward-id-comma-list t) -(def-edebug-spec c-pull-open-brace (symbolp)) - (cc-provide 'cc-engine) ;; Local Variables: |