From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: Re: [PATCH] Re: Algorithm in electric-pair--unbalanced-strings-p unsuitable for CC Mode Date: Tue, 9 Jul 2019 15:31:03 +0000 Message-ID: <20190709153103.GB5230@ACM> References: <20190708164501.GB5244@ACM> <20190708180551.GD5244@ACM> <20190709095222.GA5230@ACM> Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="70070"; mail-complaints-to="usenet@blaine.gmane.org" User-Agent: Mutt/1.10.1 (2018-07-13) Cc: =?iso-8859-1?Q?Cl=E9ment?= Pit-Claudel , emacs-devel To: =?iso-8859-1?Q?Jo=E3o_T=E1vora?= Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Jul 09 17:36:44 2019 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([209.51.188.17]) by blaine.gmane.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1hksAe-000I4v-6R for ged-emacs-devel@m.gmane.org; Tue, 09 Jul 2019 17:36:44 +0200 Original-Received: from localhost ([::1]:51170 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hksAc-0008NI-Tr for ged-emacs-devel@m.gmane.org; Tue, 09 Jul 2019 11:36:42 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:45909) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hks5L-0005Ah-8u for emacs-devel@gnu.org; Tue, 09 Jul 2019 11:31:17 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hks5I-0003Ow-UF for emacs-devel@gnu.org; Tue, 09 Jul 2019 11:31:15 -0400 Original-Received: from colin.muc.de ([193.149.48.1]:41001 helo=mail.muc.de) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1hks5I-0003Nw-Ix for emacs-devel@gnu.org; Tue, 09 Jul 2019 11:31:12 -0400 Original-Received: (qmail 93820 invoked by uid 3782); 9 Jul 2019 15:31:04 -0000 Original-Received: from acm.muc.de (p4FE15DA9.dip0.t-ipconnect.de [79.225.93.169]) by colin.muc.de (tmda-ofmipd) with ESMTP; Tue, 09 Jul 2019 17:31:03 +0200 Original-Received: (qmail 7663 invoked by uid 1000); 9 Jul 2019 15:31:03 -0000 Content-Disposition: inline In-Reply-To: X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-detected-operating-system: by eggs.gnu.org: FreeBSD 9.x [fuzzy] X-Received-From: 193.149.48.1 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:238455 Archived-At: Hello, Joćo. On Tue, Jul 09, 2019 at 11:54:39 +0100, Joćo Tįvora wrote: > On Tue, Jul 9, 2019 at 10:52 AM Alan Mackenzie wrote: > > Hello, Joćo. > > > > many, many years. Depending on how it is implemented (certainly how > > > > Alan implemented it) it breaks things in Emacs core and > > > > third-party code. > > Can we perhaps keep the disparagement a bit more muted, please? > How can this be disparagement? It's a simple statement of fact. It is an extreme interpretation of somewhat controversial facts. > If you didn't break things, what did you fix and what are you fixing? I'm fixing electric-pair-mode, and I'm adding a feature to CC Mode specially for you, because you've asked for it so insistently. :-) [ .... ] So, although the enhancement is not yet bug free, I'm supplying you with the following patch, with the request you undo your unofficial modifications to CC Mode and try out the patch. It is not 100% bug free, but might be close to it. It should allow you to do syntactic operations on two disjoint "s, while at the same time preserving CC Mode's fontification strategy. You may have less justification for complaining after trying this out. No apologies for that. ;-) diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el index a43f1ac72d..ad3cb9c125 100644 --- a/lisp/progmodes/cc-defs.el +++ b/lisp/progmodes/cc-defs.el @@ -406,6 +406,25 @@ lookup-syntax-properties (forward-sexp) (= (point) (+ 4 (point-min))))))) +(defmacro c-is-escaped (pos) + ;; Are there an odd number of backslashes before POS? + `(save-excursion + (goto-char ,pos) + (not (zerop (logand (skip-chars-backward "\\\\") 1))))) + +(defmacro c-will-be-escaped (pos beg end) + ;; Will the character after POS be escaped after the removal of (BEG END)? + ;; It is assumed that (>= POS END). + `(save-excursion + (let ((-end- ,end) + count) + (goto-char ,pos) + (setq count (skip-chars-backward "\\\\" -end-)) + (when (eq (point) -end-) + (goto-char ,beg) + (setq count (+ count (skip-chars-backward "\\\\")))) + (not (zerop (logand count 1)))))) + (defvar c-use-extents) (defmacro c-next-single-property-change (position prop &optional object limit) @@ -1019,6 +1038,14 @@ c-major-mode-is ;; properties set on a single character and that never spread to any ;; other characters. +(defmacro c-put-syn-tab (pos value) + ;; Set both the syntax-table and the c-fl-syn-tab text properties at POS to + ;; VALUE (which should not be nil). + `(let ((-pos- ,pos) + (-value- ,value)) + (c-put-char-property -pos- 'syntax-table -value-) + (c-put-char-property -pos- 'c-fl-syn-tab -value-))) + (eval-and-compile ;; Constant used at compile time to decide whether or not to use ;; XEmacs extents. Check all the extent functions we'll use since @@ -1146,6 +1173,12 @@ c-clear-char-property ;; Emacs < 21. `(c-clear-char-property-fun ,pos ',property)))) +(defmacro c-clear-syn-tab (pos) + ;; Remove both the 'syntax-table and `c-fl-syn-tab properties at POS. + `(let ((-pos- ,pos)) + (c-clear-char-property -pos- 'syntax-table) + (c-clear-char-property -pos- 'c-fl-syn-tab))) + (defmacro c-min-property-position (from to property) ;; Return the first position in the range [FROM to) where the text property ;; PROPERTY is set, or `most-positive-fixnum' if there is no such position. @@ -1381,6 +1414,29 @@ c-put-char-properties-on-char `((setq c-syntax-table-hwm (min c-syntax-table-hwm (point))))) (c-put-char-property (point) ,property ,value) (forward-char))))) + +(defmacro c-with-extended-string-fences (beg end &rest body) + ;; If needed, extend the region with "mirrored" c-fl-syn-tab properties to + ;; contain the region (BEG END), then evaluate BODY. If this mirrored + ;; region was initially empty, restore it afterwards. + `(let ((-beg- ,beg) + (-end- ,end) + ) + (cond + ((null c-fl-syn-tab-region) + (unwind-protect + (progn + (c-restore-string-fences -beg- -end-) + ,@body) + (c-clear-string-fences))) + ((and (>= -beg- (car c-fl-syn-tab-region)) + (<= -end- (cdr c-fl-syn-tab-region))) + ,@body) + (t ; Crudely extend the mirrored region. + (setq -beg- (min -beg- (car c-fl-syn-tab-region)) + -end- (max -end- (cdr c-fl-syn-tab-region))) + (c-restore-string-fences -beg- -end-) + ,@body)))) ;; Macros to put overlays (Emacs) or extents (XEmacs) on buffer text. ;; For our purposes, these are characterized by being possible to @@ -1463,6 +1519,7 @@ c-clear-char-property-with-value (def-edebug-spec c-clear-char-property-with-value-on-char t) (def-edebug-spec c-put-char-properties-on-char t) (def-edebug-spec c-clear-char-properties t) +(def-edebug-spec c-with-extended-string-fences (form form body)) (def-edebug-spec c-put-overlay t) (def-edebug-spec c-delete-overlay t) (def-edebug-spec c-mark-<-as-paren t) diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 40a3b72f6a..656dfd126c 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -155,6 +155,9 @@ (defvar c-doc-line-join-re) (defvar c-doc-bright-comment-start-re) (defvar c-doc-line-join-end-ch) +(defvar c-fl-syn-tab-region) +(cc-bytecomp-defun c-clear-string-fences) +(cc-bytecomp-defun c-restore-string-fences) ;; Make declarations for all the `c-lang-defvar' variables in cc-langs. @@ -2816,7 +2819,14 @@ c-semi-pp-to-literal c-block-comment-awkward-chars))) (and (nth 4 s) (nth 7 s) ; Line comment (not (memq (char-before here) '(?\\ ?\n))))))) - (setq s (parse-partial-sexp pos here nil nil s))) +;;;; OLD STOUGH, 2019-07-09 + ;; (setq s (parse-partial-sexp pos here nil nil s)) +;;;; NEW STOUGH, 2019-07-09 + (c-with-extended-string-fences + pos here + (setq s (parse-partial-sexp pos here nil nil s))) +;;;; END OF NEW STOUGH + ) (when (not (eq near-pos here)) (c-semi-put-near-cache-entry here s)) (cond diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 98b8385fcc..395a6b1a9d 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -181,6 +181,7 @@ c-leave-cc-mode-mode (c-save-buffer-state () (c-clear-char-properties (point-min) (point-max) 'category) (c-clear-char-properties (point-min) (point-max) 'syntax-table) + (c-clear-char-properties (point-min) (point-max) 'c-fl-syn-tab) (c-clear-char-properties (point-min) (point-max) 'c-is-sws) (c-clear-char-properties (point-min) (point-max) 'c-in-sws) (c-clear-char-properties (point-min) (point-max) 'c-type) @@ -1016,6 +1017,7 @@ c-depropertize-new-text (c-save-buffer-state () (when (> end beg) (c-clear-char-properties beg end 'syntax-table) + (c-clear-char-properties beg end 'c-fl-syn-tab) (c-clear-char-properties beg end 'category) (c-clear-char-properties beg end 'c-is-sws) (c-clear-char-properties beg end 'c-in-sws) @@ -1205,6 +1207,43 @@ c-multiline-string-check-final-quote (c-put-char-property (1- (point)) 'syntax-table '(15))) (t nil))))) +(defvar c-fl-syn-tab-region nil) + ;; Non-nil when a `c-restore-string-fences' is "in force". It's value is a + ;; cons of the BEG and END of the region currently "mirroring" the + ;; c-fl-syn-tab properties as syntax-table properties. + +(defun c-clear-string-fences () + ;; Clear any syntax-table text properties in the region defined by + ;; `c-fl-syn-tab-region' which are "mirrored" by c-fl-syn-tab text + ;; properties. + (when c-fl-syn-tab-region + (let ((pos (car c-fl-syn-tab-region)) + (end (cdr c-fl-syn-tab-region))) + (while + (and + (< pos end) + (setq pos + (c-min-property-position pos end 'c-fl-syn-tab)) + (< pos end)) + (c-clear-char-property pos 'syntax-table) + (setq pos (1+ pos))) + (setq c-fl-syn-tab-region nil)))) + +(defun c-restore-string-fences (beg end) + ;; Restore any syntax-table text properties in the region (BEG END) which + ;; are "mirrored" by c-fl-syn-tab text properties. + (let ((pos beg)) + (while + (and + (< pos end) + (setq pos + (c-min-property-position pos end 'c-fl-syn-tab)) + (< pos end)) + (c-put-char-property pos 'syntax-table + (c-get-char-property pos 'c-fl-syn-tab)) + (setq pos (1+ pos))) + (setq c-fl-syn-tab-region (cons beg end)))) + (defvar c-bc-changed-stringiness nil) ;; Non-nil when, in a before-change function, the deletion of a range of text ;; will change the "stringiness" of the subsequent text. Only used when @@ -1261,7 +1300,7 @@ c-before-change-check-unbalanced-strings "\"\\|\\s|") (point-max) t t) (progn - (c-clear-char-property (1- (point)) 'syntax-table) + (c-clear-syn-tab (1- (point))) (c-truncate-lit-pos-cache (1- (point))) (not (memq (char-before) c-string-delims))))) (memq (char-before) c-string-delims)) @@ -1291,10 +1330,8 @@ c-before-change-check-unbalanced-strings (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-will-be-escaped end beg end)) + (c-clear-syn-tab end) (c-truncate-lit-pos-cache end) (goto-char (1+ end))) ;; Are we unescaping a newline by inserting stuff between \ and \n? @@ -1317,15 +1354,15 @@ c-before-change-check-unbalanced-strings (let ((eoll-1 (point))) (forward-char) (backward-sexp) - (c-clear-char-property eoll-1 'syntax-table) - (c-clear-char-property (point) 'syntax-table) + (c-clear-syn-tab eoll-1) + (c-clear-syn-tab (point)) (c-truncate-lit-pos-cache (point))) ;; Opening " at EOB. - (c-clear-char-property (1- (point)) 'syntax-table)) + (c-clear-syn-tab (1- (point)))) (when (and (c-search-backward-char-property 'syntax-table '(15) c-new-BEG) (memq (char-after) c-string-delims)) ; Ignore an unterminated raw string's (. ;; Opening " on last line of text (without EOL). - (c-clear-char-property (point) 'syntax-table) + (c-clear-syn-tab (point)) (c-truncate-lit-pos-cache (point)) (setq c-new-BEG (min c-new-BEG (point)))))) @@ -1334,7 +1371,7 @@ c-before-change-check-unbalanced-strings (and (c-search-backward-char-property 'syntax-table '(15) c-new-BEG) (memq (char-after) c-string-delims)) - (c-clear-char-property (point) 'syntax-table) + (c-clear-syn-tab (point)) (c-truncate-lit-pos-cache (point))))) (unless @@ -1346,13 +1383,13 @@ c-before-change-check-unbalanced-strings (not (c-characterp c-multiline-string-start-char)))) (when (and (eq end-literal-type 'string) (not (eq (char-before (cdr end-limits)) ?\())) - (c-clear-char-property (1- (cdr end-limits)) 'syntax-table) + (c-clear-syn-tab (1- (cdr end-limits))) (c-truncate-lit-pos-cache (1- (cdr end-limits))) (setq c-new-END (max c-new-END (cdr end-limits)))) (when (and (eq beg-literal-type 'string) (memq (char-after (car beg-limits)) c-string-delims)) - (c-clear-char-property (car beg-limits) 'syntax-table) + (c-clear-syn-tab (car beg-limits)) (c-truncate-lit-pos-cache (car beg-limits)) (setq c-new-BEG (min c-new-BEG (car beg-limits))))))) @@ -1375,7 +1412,7 @@ c-after-change-mark-abnormal-strings end-literal-limits end-literal-type) (when (and (eq beg-literal-type 'string) (c-get-char-property (car beg-literal-limits) 'syntax-table)) - (c-clear-char-property (car beg-literal-limits) 'syntax-table) + (c-clear-syn-tab (car beg-literal-limits)) (setq c-bc-changed-stringiness (not c-bc-changed-stringiness))) (setq end-literal-limits (progn (goto-char end) (c-literal-limits)) end-literal-type (c-literal-type end-literal-limits)) @@ -1456,13 +1493,13 @@ c-after-change-mark-abnormal-strings (looking-at (cdr (assq (char-before) c-string-innards-re-alist)))) (cond ((memq (char-after (match-end 0)) '(?\n ?\r)) - (c-put-char-property (1- (point)) 'syntax-table '(15)) - (c-put-char-property (match-end 0) 'syntax-table '(15)) + (c-put-syn-tab (1- (point)) '(15)) + (c-put-syn-tab (match-end 0) '(15)) (setq c-new-BEG (min c-new-BEG (point)) c-new-END (max c-new-END (match-end 0)))) ((or (eq (match-end 0) (point-max)) (eq (char-after (match-end 0)) ?\\)) ; \ at EOB - (c-put-char-property (1- (point)) 'syntax-table '(15)) + (c-put-syn-tab (1- (point)) '(15)) (setq c-new-BEG (min c-new-BEG (point)) c-new-END (max c-new-END (match-end 0))) ; Do we need c-new-END? )) @@ -1506,16 +1543,16 @@ c-after-change-escape-NL-in-string nil t) (eq (char-after) ?\") (equal (c-get-char-property (point) 'syntax-table) '(15))) - (c-clear-char-property end 'syntax-table) + (c-clear-syn-tab end) (c-truncate-lit-pos-cache end) - (c-clear-char-property (point) 'syntax-table) + (c-clear-syn-tab (point)) (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)))))) + (c-clear-syn-tab (point))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Parsing of quotes. @@ -1794,6 +1831,11 @@ c-before-change ;; property changes. (when (fboundp 'syntax-ppss) (setq c-syntax-table-hwm most-positive-fixnum)) +;;;; NEW STOUGH, 2019-07-09 + (unwind-protect + (progn + (c-restore-string-fences (point-min) (point-max)) +;;;; END OF NEW STOUGH (save-restriction (save-match-data (widen) @@ -1865,7 +1907,12 @@ c-before-change ))) ;; The following must be done here rather than in `c-after-change' because ;; newly inserted parens would foul up the invalidation algorithm. - (c-invalidate-state-cache beg))) + (c-invalidate-state-cache beg) +;;;; NEW STOUGH, 2019-07-09 + ) + (c-clear-string-fences)) +;;;; END OF NEW STOUGH + )) (defvar c-in-after-change-fontification nil) (make-variable-buffer-local 'c-in-after-change-fontification) @@ -1909,6 +1956,11 @@ c-after-change ;; When `combine-after-change-calls' is used we might get calls ;; with regions outside the current narrowing. This has been ;; observed in Emacs 20.7. +;;;; NEW STOUGH, 2019-07-09 + (unwind-protect + (progn + (c-restore-string-fences (point-min) (point-max)) +;;;; END OF NEW STOUGH (save-restriction (save-match-data ; c-recognize-<>-arglists changes match-data (widen) @@ -1945,7 +1997,12 @@ c-after-change (save-excursion (mapc (lambda (fn) (funcall fn beg end old-len)) - c-before-font-lock-functions)))))) + c-before-font-lock-functions)))) +;;;; NEW STOUGH, 2019-07-09 + ) + (c-clear-string-fences)) +;;;; END OF NEW STOUGH + )) ;; A workaround for syntax-ppss's failure to notice syntax-table text ;; property changes. (when (fboundp 'syntax-ppss) @@ -2173,8 +2230,11 @@ c-font-lock-fontify-region ;; Context (etc.) fontification. (setq new-region (c-before-context-fl-expand-region beg end) new-beg (car new-region) new-end (cdr new-region))) - (funcall (default-value 'font-lock-fontify-region-function) - new-beg new-end verbose))) + (unwind-protect + (progn (c-restore-string-fences new-beg new-end) + (funcall (default-value 'font-lock-fontify-region-function) + new-beg new-end verbose)) + (c-clear-string-fences)))) (defun c-after-font-lock-init () ;; Put on `font-lock-mode-hook'. This function ensures our after-change @@ -2291,7 +2351,7 @@ c-electric-pair-inhibit-predicate invalid strings with such a syntax table text property on the opening \" and the next unescaped end of line." (if (eq char ?\") - (not (equal (get-text-property (1- (point)) 'syntax-table) '(15))) + (not (equal (get-text-property (1- (point)) 'c-fl-syn-tab) '(15))) (funcall (default-value 'electric-pair-inhibit-predicate) char))) > Joćo -- Alan Mackenzie (Nuremberg, Germany).