Subject: bug#49629: 27.2; electric-pair-mode doesn't work for angle brackets in HTML file

From: Allen Li <[email-masked]>
Date: Sat, 2 Jul 2022 03:48:31 -0700

How does the attached patch look?

I did not clear the syntax patch for the entire buffer because I was
worried of the performance impact.
We assume that we won't be touching the part of the buffer before the
specified start.
IIUC there is no issue keeping the cached state from before start using the
original table,
as we only want to consider the text succeeding start as text.

On Mon, Jun 27, 2022 at 5:39 AM Stefan Monnier <[email-masked]>
wrote:

> > FIrst, changing `syntax-propertize-function` from nil to `ignore` was
> wrong
> > IIUC.  If the function is set, then it is wholly responsible for applying
> > syntax table.  When set to nil the default behavior is used, but when set
> > to `ignore`, that should mean that no syntax is applied at all.  In
> > practice, I don't know what behavior that causes.
>
> The two behave identically (IOW the "default behavior" is the same as
> that of `ignore`).
>
> > Second, since `electric-pair--with-uncached-syntax` appears to be used
> for
> > doing text-mode matching (as you've also observed), maybe we should
> > de-generalize it to do only that.  I think that allows us to make some
> > simplifying assumptions about the state of the world.
>
> Sounds good.
>
>
>         Stefan
>
>
From fc3a85fc83fa3fd54a73002575d339e6bb7423b2 Mon Sep 17 00:00:00 2001
From: Allen Li <[email-masked]>
Date: Sun, 26 Jun 2022 02:32:47 -0700
Subject: [PATCH] elec-pair: Fix bug incorrectly hiding
 syntax-propertize-function

The main bug that this is fixing is `syntax-propertize-function' being
hidden in `electric-pair--balance-info' when the original syntax table
is to be used, not `electric-pair-text-syntax-table'.

Notably, this causes `electric-pair-mode' to often misbehave in HTML
files when pairing angle brackets.

This commit also flushes the cache before installing
`electric-pair-text-syntax-table', to prevent cached syntax for the
original table from affecting things.
---
 lisp/elec-pair.el | 82 +++++++++++++++++++++++++----------------------
 1 file changed, 43 insertions(+), 39 deletions(-)

diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el
index bbed955a39..dab37c5110 100644
--- a/lisp/elec-pair.el
+++ b/lisp/elec-pair.el
@@ -188,6 +188,25 @@ electric-pair-conservative-inhibit
    ;; I also find it often preferable not to pair next to a word.
    (eq (char-syntax (following-char)) ?w)))
 
+(cl-defmacro electric-pair--with-text-syntax ((&optional start) &rest body)
+  "Run BODY with `electric-pair-text-syntax-table' active.
+This ensures that all syntax related values are set properly and the
+`syntax-ppss' cache is cleared before and after.
+In particular, this must be used when BODY contains code which may
+update the `syntax-ppss' cache.  This includes calling
+`parse-partial-sexp' and any sexp-based movement functions when
+`parse-sexp-lookup-properties' is non-nil.  The cache is flushed from
+position START, defaulting to point."
+  (declare (debug ((&optional form) body)) (indent 1))
+  (let ((start-var (make-symbol "start")))
+    `(let ((syntax-propertize-function nil)
+           (,start-var ,(or start '(point))))
+       (syntax-ppss-flush-cache ,start-var)
+       (unwind-protect
+           (with-syntax-table electric-pair-text-syntax-table
+             ,@body)
+         (syntax-ppss-flush-cache ,start-var)))))
+
 (defun electric-pair-syntax-info (command-event)
   "Calculate a list (SYNTAX PAIR UNCONDITIONAL STRING-OR-COMMENT-START).
 
@@ -202,13 +221,12 @@ electric-pair-syntax-info
          (post-string-or-comment (nth 8 (syntax-ppss (point))))
          (string-or-comment (and post-string-or-comment
                                  pre-string-or-comment))
-         (table (if string-or-comment
-                    electric-pair-text-syntax-table
-                  (syntax-table)))
-         (table-syntax-and-pair (with-syntax-table table
-                                  (list (char-syntax command-event)
-                                        (or (matching-paren command-event)
-                                            command-event))))
+         (table-syntax-and-pair (cl-flet ((f () (list (char-syntax command-event)
+                                                      (or (matching-paren command-event)
+                                                          command-event))))
+                                  (if string-or-comment
+                                      (electric-pair--with-text-syntax () (f))
+                                    (f))))
          (fallback (if string-or-comment
                        (append electric-pair-text-pairs
                                electric-pair-pairs)
@@ -237,22 +255,6 @@ electric-pair--insert
         (electric-layout-allow-duplicate-newlines t))
     (self-insert-command 1)))
 
-(cl-defmacro electric-pair--with-uncached-syntax ((table &optional start) &rest body)
-  "Like `with-syntax-table', but flush the `syntax-ppss' cache afterwards.
-Use this instead of (with-syntax-table TABLE BODY) when BODY
-contains code which may update the `syntax-ppss' cache.  This
-includes calling `parse-partial-sexp' and any sexp-based movement
-functions when `parse-sexp-lookup-properties' is non-nil.  The
-cache is flushed from position START, defaulting to point."
-  (declare (debug ((form &optional form) body)) (indent 1))
-  (let ((start-var (make-symbol "start")))
-    `(let ((syntax-propertize-function #'ignore)
-           (,start-var ,(or start '(point))))
-       (unwind-protect
-           (with-syntax-table ,table
-             ,@body)
-         (syntax-ppss-flush-cache ,start-var)))))
-
 (defun electric-pair--syntax-ppss (&optional pos where)
   "Like `syntax-ppss', but sometimes fallback to `parse-partial-sexp'.
 
@@ -271,8 +273,7 @@ electric-pair--syntax-ppss
                               (skip-syntax-forward " >!")
                               (point)))))
     (if s-or-c-start
-        (electric-pair--with-uncached-syntax (electric-pair-text-syntax-table
-                                              s-or-c-start)
+        (electric-pair--with-text-syntax (s-or-c-start)
           (parse-partial-sexp s-or-c-start pos))
       ;; HACK! cc-mode apparently has some `syntax-ppss' bugs
       (if (memq major-mode '(c-mode c++ mode))
@@ -301,9 +302,6 @@ electric-pair--balance-info
 If point is not enclosed by any lists, return ((t) . (t))."
   (let* (innermost
          outermost
-         (table (if string-or-comment
-                    electric-pair-text-syntax-table
-                  (syntax-table)))
          (at-top-level-or-equivalent-fn
           ;; called when `scan-sexps' ran perfectly, when it found
           ;; a parenthesis pointing in the direction of travel.
@@ -325,11 +323,12 @@ electric-pair--balance-info
                         (cond ((< direction 0)
                                (condition-case nil
                                    (eq (char-after pos)
-                                       (electric-pair--with-uncached-syntax
-                                           (table)
-                                         (matching-paren
-                                          (char-before
-                                           (scan-sexps (point) 1)))))
+                                       (cl-flet ((f () (matching-paren
+                                                        (char-before
+                                                         (scan-sexps (point) 1)))))
+                                         (if string-or-comment
+                                             (electric-pair--with-text-syntax () (f))
+                                           (f))))
                                  (scan-error nil)))
                               (t
                                ;; In this case, no need to use
@@ -343,7 +342,9 @@ electric-pair--balance-info
                                       (opener (char-after start)))
                                  (and start
                                       (eq (char-before pos)
-                                          (or (with-syntax-table table
+                                          (or (if string-or-comment
+                                                  (electric-pair--with-text-syntax ()
+                                                    (matching-paren opener))
                                                 (matching-paren opener))
                                               opener))))))))
                      (actual-pair (if (> direction 0)
@@ -356,11 +357,14 @@ electric-pair--balance-info
     (save-excursion
       (while (not outermost)
         (condition-case err
-            (electric-pair--with-uncached-syntax (table)
-              (scan-sexps (point) (if (> direction 0)
-                                      (point-max)
-                                    (- (point-max))))
-              (funcall at-top-level-or-equivalent-fn))
+            (cl-flet ((f ()
+                         (scan-sexps (point) (if (> direction 0)
+                                                 (point-max)
+                                               (- (point-max))))
+                         (funcall at-top-level-or-equivalent-fn)))
+              (if string-or-comment
+                  (electric-pair--with-text-syntax () (f))
+                (f)))
           (scan-error
            (cond ((or
                    ;; some error happened and it is not of the "ended
-- 
2.37.0