Subject: bug#78053: [PATCH] Support strings for electric-pairs.

From: Stefan Monnier <[email-masked]>
Date: Sun, 25 May 2025 09:44:45 -0400

> As discussed in bug#77823, this feature bring support for
> using strings as pairs in `electric-pair-mode',

Thank you for looking into it.  I think this would be a nice addition to
`electric-pair-mode`.

> +---
> +** Electric pair mode now supports using strings as pairs.
> +Now 'electric-pair-mode' can use strings as pairs,
> +TODO: I don't know what to put here

The second line just repeats the first, so you can just remove it.
In general, this seems sufficient, tho maybe we should clarify the
"meaning" of using strings there.  IOW, I'd say something like:

    ** Electric pair mode supports multicharacter paired delimiters.
    This is done by supporting strings in 'electric-pair-pairs'.

> @@ -41,7 +41,9 @@ electric-pair-pairs
>  See also the variable `electric-pair-text-pairs'."
>    :version "24.1"
>    :group 'electricity
> -  :type '(repeat (cons character character)))
> +  :type '(repeat
> +          (choice (cons character character)
> +                  (cons string string))))

We want to do the same for `electric-pair-text-pairs`, no?

> @@ -258,7 +260,18 @@ electric-pair-syntax-info
>       (direct (if (eq (car direct) (cdr direct))
>                   (list ?\" command-event t string-or-comment)
>                 (list ?\( (cdr direct) t string-or-comment)))
> -     (reverse (list ?\) (car reverse) t string-or-comment)))))
> +     (reverse (list ?\) (car reverse) t string-or-comment))
> +     ;; The if-let here is for avoid unnecessary looking-back computation
> +     ((if-let* ((str-pair
> +		 (cl-loop for pairs in fallback
> +			  do
> +			  (if (and
> +			       (stringp (car pairs))
> +			       (looking-back
> +				(regexp-quote (car pairs))
> +				(- (point) (length (car pairs)))))
> +			      (cl-return (cdr pairs))))))
> +	  (list 'str str-pair nil nil))))))

BTW, here you could use `compare-buffer-substrings`.

And you can get rid of the `if-let*` altogether by doing:

     (t (cl-loop
         for pairs in fallback
         do (when {...}
              (cl-return (list 'str ...))))))))

>  (defun electric-pair--insert (char times)
>    (let ((last-command-event char)
> @@ -591,6 +604,11 @@ electric-pair-post-self-insert-function
>                                pos))
>           (forward-char num))
>          ;; Insert matching pair.
> +        ;; String pairs
> +        ((and (eq syntax 'str) (not overwrite-mode))
> +         (insert " ")
> +         (save-excursion (insert " " pair)))
> +        ;; Char pairs
>          ((and (memq syntax '(?\( ?\" ?\$))
>                (not overwrite-mode)
>                (or unconditional

I can imagine some multi-char delimiters which don't call for spaces
inside of them (e.g. LaTeX's ``...''), so maybe the entries in
`electric-pair-pairs` should have some extra info about it.
I suggest we replace the (STRING . STRING) format with
(STRING STRING ...) so we can put extra info in the `...`.

        Stefan