From b25d58c956a9fcd2b81b804699573ea851bd8fde Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Tue, 2 Jul 2019 12:33:01 +0000 Subject: [PATCH] CC Mode: Improve handling of unbalanced strings * lisp/progmodes/cc-fonts.el (c-before-font-lock-functions): Add function c-after-change-escape-NL-in-string into value for most languages. * lisp/progmodes/cc-mode.el (c-after-change-escape-NL-in-string): New function. (c-before-change-check-unbalanced-strings): Handle the making and breaking of escaped newlines, by removal or addition of text. --- lisp/progmodes/cc-langs.el | 6 ++- lisp/progmodes/cc-mode.el | 78 +++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index 2fcd6acf51f..153d3fc2608 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -478,7 +478,7 @@ so that all identifiers are recognized as words.") (list fs))) "If non-nil, a list of functions called from c-before-change-hook. Typically these will record enough state to allow -`c-before-font-lock-function' to extend the region to fontify, +`c-before-font-lock-functions' to extend the region to fontify, and may do such things as removing text-properties which must be recalculated. @@ -497,15 +497,18 @@ parameters \(point-min) and \(point-max).") ;; For documentation see the following c-lang-defvar of the same name. ;; The value here may be a list of functions or a single function. t '(c-depropertize-new-text + c-after-change-escape-NL-in-string c-after-change-mark-abnormal-strings c-change-expand-fl-region) (c objc) '(c-depropertize-new-text + c-after-change-escape-NL-in-string c-parse-quotes-after-change c-after-change-mark-abnormal-strings c-extend-font-lock-region-for-macros c-neutralize-syntax-in-CPP c-change-expand-fl-region) c++ '(c-depropertize-new-text + c-after-change-escape-NL-in-string c-after-change-unmark-raw-strings c-parse-quotes-after-change c-after-change-mark-abnormal-strings @@ -514,6 +517,7 @@ parameters \(point-min) and \(point-max).") c-restore-<>-properties c-change-expand-fl-region) java '(c-depropertize-new-text + c-after-change-escape-NL-in-string c-parse-quotes-after-change c-after-change-mark-abnormal-strings c-restore-<>-properties diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 5c18879712c..8f4bb341acb 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -1262,11 +1262,31 @@ Note that the style variables are always made local to the buffer." (setq c-new-BEG (min (car beg-limits) c-new-BEG)))) ((< end (point-max)) - (goto-char (1+ end)) ; might be a newline. - ;; In the following regexp, the initial \n caters for a newline getting - ;; joined to a preceding \ by the removal of what comes between. - (re-search-forward "[\n\r]?\\(\\\\\\(.\\|\n\\)\\|[^\\\n\r]\\)*" - nil t) + ;; Have we just escaped a newline by deleting characters? + (if (and (eq end-literal-type 'string) + (memq (char-after end) '(?\n ?\r))) + (cond + ;; Are we escaping a newline by deleting stuff between \ and \n? + ((and (> end beg) + (progn + (goto-char end) + (eq (logand (skip-chars-backward "\\\\" beg) 1) 1))) + (c-clear-char-property end 'syntax-table) + (c-truncate-lit-pos-cache end) + (goto-char (1+ end))) + ;; Are we unescaping a newline by inserting stuff between \ and \n? + ((and (eq end beg) + (progn + (goto-char end) + (eq (logand (skip-chars-backward "\\\\") 1) 1))) + (goto-char (1+ end))) ; To after the NL which is being unescaped. + (t + (goto-char end))) + (goto-char end)) + + ;; Move to end of logical line (as it will be after the change, or as it + ;; was before unescaping a NL.) + (re-search-forward "\\(\\\\\\(.\\|\n\\|\r\\)\\|[^\\\n\r]\\)*" nil t) ;; We're at an EOLL or point-max. (if (equal (c-get-char-property (point) 'syntax-table) '(15)) (if (memq (char-after) '(?\n ?\r)) @@ -1426,6 +1446,54 @@ Note that the style variables are always made local to the buffer." (goto-char (min (1+ (match-end 0)) (point-max)))) (setq s nil))))) +(defun c-after-change-escape-NL-in-string (beg end _old_len) + ;; If a backslash has just been inserted into a string, and this quotes an + ;; existing newline, remove the string fence syntax-table text properties + ;; on what has become the tail of the string. + ;; + ;; POINT is undefined both at entry to and exit from this function, the + ;; buffer will have been widened, and match data will have been saved. + ;; + ;; This function is called exclusively as an after-change function via + ;; `c-before-font-lock-functions'. In C++ Mode, it should come before + ;; `c-after-change-unmark-raw-strings' in that lang variable. + (let (lit-start) ; Don't calculate this till we have to. + (when + (and (> end beg) + (memq (char-after end) '(?\n ?\r)) + (progn (goto-char end) + (eq (logand (skip-chars-backward "\\\\") 1) 1)) + (progn (goto-char end) + (setq lit-start (c-literal-start))) + (memq (char-after lit-start) c-string-delims) + (or (not (c-major-mode-is 'c++-mode)) + (progn + (goto-char lit-start) + (and (not (and (eq (char-before) ?R) + (looking-at c-c++-raw-string-opener-1-re))) + (not (and (eq (char-after) ?\() + (equal (c-get-char-property + (point) 'syntax-table) + '(15)))))) + (save-excursion + (c-beginning-of-macro)))) + (goto-char (1+ end)) ; After the \ + ;; Search forward for a closing ". + (when (and (re-search-forward "\\(\\\\\\(.\\|\n\\)\\|[^\"\\\n\r]\\)*" + nil t) + (eq (char-after) ?\") + (equal (c-get-char-property (point) 'syntax-table) '(15))) + (c-clear-char-property end 'syntax-table) + (c-truncate-lit-pos-cache end) + (c-clear-char-property (point) 'syntax-table) + (forward-char) ; to after the " + (when + (and + ;; Search forward for an end of logical line. + (re-search-forward "\\(\\\\\\(.\\|\n\\)\\|[^\\\n\r]\\)*" nil t) + (memq (char-after) '(?\n ?\r))) + (c-clear-char-property (point) 'syntax-table)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Parsing of quotes. ;; -- 2.39.2