From: Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
To: Joseph Turner <joseph@breatheoutbreathe.in>
Cc: 72437@debbugs.gnu.org, eliz@gnu.org, monnier@iro.umontreal.ca
Subject: bug#72437: Unexpected behavior when inserting with prefix arg in electric-pair-mode
Date: Sat, 21 Sep 2024 12:23:55 +0200 [thread overview]
Message-ID: <m1jzf55mpw.fsf@eshelyaron.com> (raw)
In-Reply-To: <87plp0w7g7.fsf@breatheoutbreathe.in> (Joseph Turner's message of "Wed, 18 Sep 2024 22:14:48 -0700")
[-- Attachment #1: Type: text/plain, Size: 1214 bytes --]
Joseph Turner <joseph@breatheoutbreathe.in> writes:
> Joseph Turner <joseph@breatheoutbreathe.in> writes:
>
>> On August 13, 2024 10:07:23 PM PDT, Eshel Yaron <me@eshelyaron.com> wrote:
>>>Hi Stefan,
>>>
>>>Stefan Monnier writes:
>>>
>>>>> Let's hear what Stefan thinks (and anyone else who has an
>>>>> opinion or some case other than electric-pair-mode).
>>>>
>>>> I think the requested behavior makes a lot of sense, but I lost touch
>>>> with the way elec-pair works and interacts with other electric modes, so
>>>> I'm not sure how to make it work without breaking other things.
>>>
>>>In case you missed it, the patch I shared upthread might give you
>>>some ideas. It implements the requested behavior, safely, I think.
>>>So I would just ask Joseph to try it out for a while and see if it
>>>works as expected and if any other issues crop up :)
>>
>> Thanks for this patch! I am testing it in my daily config. So far
>> so good, and I'll report back after a few weeks of usage.
>
> I have been running this patch for over a month with no issue.
That's great, thanks for testing it and for reporting back!
> I vote to merge it!
Agreed. Here's a full patch with tests, doc updates and a NEWS entry:
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Teach-Electric-Pair-mode-about-prefix-arguments.patch --]
[-- Type: text/x-patch, Size: 10834 bytes --]
From 8e732a6db9cc2855c2b0f307129555bb2551ff96 Mon Sep 17 00:00:00 2001
From: Eshel Yaron <me@eshelyaron.com>
Date: Sat, 21 Sep 2024 12:15:16 +0200
Subject: [PATCH] Teach Electric Pair mode about prefix arguments
* lisp/elec-pair.el (electric-pair--insert): New arg TIMES.
(electric-pair-inhibit-if-helps-balance)
(electric-pair-post-self-insert-function)
(electric-pair-open-newline-between-pairs-psif)
(electric-pair-skip-if-helps-balance): Respect prefix arg.
(electric-pair-delete-pair): Delete ARG chars, not just 1.
* test/lisp/electric-tests.el (autowrapping-multi-1)
(autowrapping-multi-2): New tests.
* doc/emacs/programs.texi (Matching): Mention prefix arg
support in Electric Pair mode documentation.
* etc/NEWS: Announce it.
---
doc/emacs/programs.texi | 4 +-
etc/NEWS | 6 +++
lisp/elec-pair.el | 87 ++++++++++++++++++++-----------------
test/lisp/electric-tests.el | 16 +++++++
4 files changed, 72 insertions(+), 41 deletions(-)
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 42b57143bf1..948f4128acf 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -1061,7 +1061,9 @@ Matching
over. If the region is active (@pxref{Mark}), insertion of a
delimiter operates on the region: the characters in the region are
enclosed in a pair of matching delimiters, leaving point after the
-delimiter you typed.
+delimiter you typed. If you provide a prefix argument when inserting
+a delimiter, the numeric value of that prefix argument specifies the
+number of pairs to insert.
These variables control additional features of Electric Pair mode:
diff --git a/etc/NEWS b/etc/NEWS
index b902aaf7ead..f22215851a6 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -147,6 +147,12 @@ instead of raising an error. Note that if you have disabled Transient
Mark mode you might prefer to use 'unix-word-rubout', as this feature
relies on there being an active region.
++++
+** Electric Pair mode can now pair multiple delimiters at once.
+You can now insert or wrap text with multiple sets of parentheses and
+other matching delimiters at once with Electric Pair mode, by providing
+a prefix argument when inserting one of the delimiters.
+
\f
* Changes in Specialized Modes and Packages in Emacs 31.1
diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el
index 40618e9ef38..1fae0ba6ba4 100644
--- a/lisp/elec-pair.el
+++ b/lisp/elec-pair.el
@@ -260,7 +260,7 @@ electric-pair-syntax-info
(list ?\( (cdr direct) t string-or-comment)))
(reverse (list ?\) (car reverse) t string-or-comment)))))
-(defun electric-pair--insert (char)
+(defun electric-pair--insert (char times)
(let ((last-command-event char)
(blink-matching-paren nil)
(electric-pair-mode nil)
@@ -271,7 +271,7 @@ electric-pair--insert
;; us to add these newlines, and is probably about to kick in
;; again after we add the closer.
(electric-layout-allow-duplicate-newlines t))
- (self-insert-command 1)))
+ (self-insert-command times)))
(defun electric-pair--syntax-ppss (&optional pos where)
"Like `syntax-ppss', but sometimes fallback to `parse-partial-sexp'.
@@ -455,7 +455,8 @@ electric-pair-inhibit-if-helps-balance
(atomic-change-group
;; Don't use `delete-char'; that may modify the head of the
;; undo list.
- (delete-region (point) (1- (point)))
+ (delete-region (- (point) (prefix-numeric-value current-prefix-arg))
+ (point))
(throw
'done
(cond ((eq ?\( syntax)
@@ -474,25 +475,26 @@ electric-pair-skip-if-helps-balance
Works by first removing the character from the buffer, then doing
some list calculations, finally restoring the situation as if nothing
happened."
- (pcase (electric-pair-syntax-info char)
- (`(,syntax ,pair ,_ ,s-or-c)
- (unwind-protect
- (progn
- (delete-char -1)
- (cond ((eq syntax ?\))
- (let* ((pair-data
- (electric-pair--balance-info
- -1 s-or-c))
- (innermost (car pair-data))
- (outermost (cdr pair-data)))
- (and
- (cond ((car outermost)
- (car innermost))
- ((car innermost)
- (not (eq (cdr outermost) pair)))))))
- ((eq syntax ?\")
- (electric-pair--inside-string-p char))))
- (insert char)))))
+ (let ((num (prefix-numeric-value current-prefix-arg)))
+ (pcase (electric-pair-syntax-info char)
+ (`(,syntax ,pair ,_ ,s-or-c)
+ (unwind-protect
+ (progn
+ (delete-char (- num))
+ (cond ((eq syntax ?\))
+ (let* ((pair-data
+ (electric-pair--balance-info
+ (- num) s-or-c))
+ (innermost (car pair-data))
+ (outermost (cdr pair-data)))
+ (and
+ (cond ((car outermost)
+ (car innermost))
+ ((car innermost)
+ (not (eq (cdr outermost) pair)))))))
+ ((eq syntax ?\")
+ (electric-pair--inside-string-p char))))
+ (insert (make-string num char)))))))
(defun electric-pair-default-skip-self (char)
(if electric-pair-preserve-balance
@@ -527,11 +529,14 @@ electric-pair-post-self-insert-function
`electric-pair-inhibit-predicate', `electric-pair-skip-self'
and `electric-pair-skip-whitespace' (which see)."
(let* ((pos (and electric-pair-mode (electric--after-char-pos)))
+ (num (when pos (prefix-numeric-value current-prefix-arg)))
+ (beg (when num (- pos num)))
(skip-whitespace-info))
(pcase (electric-pair-syntax-info last-command-event)
(`(,syntax ,pair ,unconditional ,_)
(cond
((null pos) nil)
+ ((zerop num) nil)
;; Wrap a pair around the active region.
;;
((and (memq syntax '(?\( ?\) ?\" ?\$)) (use-region-p))
@@ -545,23 +550,24 @@ electric-pair-post-self-insert-function
(>= (mark) (point))))
(save-excursion
(goto-char (mark))
- (electric-pair--insert pair))
- (delete-region pos (1- pos))
- (electric-pair--insert pair)
+ (electric-pair--insert pair num))
+ (delete-region beg pos)
+ (electric-pair--insert pair num)
(goto-char (mark))
- (electric-pair--insert last-command-event)))
+ (electric-pair--insert last-command-event num)))
;; Backslash-escaped: no pairing, no skipping.
((save-excursion
- (goto-char (1- pos))
+ (goto-char beg)
(not (zerop (% (skip-syntax-backward "\\") 2))))
- nil)
+ (let ((current-prefix-arg (1- num)))
+ (electric-pair-post-self-insert-function)))
;; Skip self.
((and (memq syntax '(?\) ?\" ?\$))
(and (or unconditional
(if (functionp electric-pair-skip-self)
(electric-pair--save-literal-point-excursion
- (goto-char pos)
- (funcall electric-pair-skip-self last-command-event))
+ (goto-char pos)
+ (funcall electric-pair-skip-self last-command-event))
electric-pair-skip-self))
(save-excursion
(when (and (not (and unconditional
@@ -580,19 +586,19 @@ electric-pair-post-self-insert-function
;; live with it for now.
(when skip-whitespace-info
(funcall electric-pair-skip-whitespace-function))
- (delete-region (1- pos) (if (eq skip-whitespace-info 'chomp)
- (point)
- pos))
- (forward-char))
+ (delete-region beg (if (eq skip-whitespace-info 'chomp)
+ (point)
+ pos))
+ (forward-char num))
;; Insert matching pair.
((and (memq syntax '(?\( ?\" ?\$))
(not overwrite-mode)
(or unconditional
(not (electric-pair--save-literal-point-excursion
- (goto-char pos)
- (funcall electric-pair-inhibit-predicate
- last-command-event)))))
- (save-excursion (electric-pair--insert pair))))))))
+ (goto-char pos)
+ (funcall electric-pair-inhibit-predicate
+ last-command-event)))))
+ (save-excursion (electric-pair--insert pair num))))))))
(defun electric-pair-open-newline-between-pairs-psif ()
"Honor `electric-pair-open-newline-between-pairs'.
@@ -604,7 +610,8 @@ electric-pair-open-newline-between-pairs-psif
(< (1+ (point-min)) (point) (point-max))
(eq (save-excursion
(skip-chars-backward "\t\s")
- (char-before (1- (point))))
+ (char-before (- (point)
+ (prefix-numeric-value current-prefix-arg))))
(matching-paren (char-after))))
(save-excursion (newline 1 t))))
@@ -618,7 +625,7 @@ electric-pair-delete-pair
ARG and KILLP are passed directly to
`backward-delete-char-untabify', which see."
(interactive "*p\nP")
- (delete-char 1)
+ (delete-char arg)
(backward-delete-char-untabify arg killp))
(defvar electric-pair-mode-map
diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el
index 720313511a6..76c18594b26 100644
--- a/test/lisp/electric-tests.el
+++ b/test/lisp/electric-tests.el
@@ -653,6 +653,22 @@ autowrapping-7
(skip-chars-backward "\"")
(mark-sexp -1)))
+(define-electric-pair-test autowrapping-multi-1
+ "foo" "(" :expected-string "(((((foo)))))" :expected-point 6
+ :bindings '((current-prefix-arg . 5))
+ :fixture-fn (lambda ()
+ (electric-pair-mode 1)
+ (mark-sexp 1)))
+
+(define-electric-pair-test autowrapping-multi-2
+ "foo" ")" :expected-string "(((((foo)))))" :expected-point 14
+ :bindings '((current-prefix-arg . 5))
+ :fixture-fn (lambda ()
+ (electric-pair-mode 1)
+ (goto-char (point-max))
+ (skip-chars-backward "\"")
+ (mark-sexp -1)))
+
\f
;;; Electric quotes
(define-electric-pair-test electric-quote-string
--
2.46.0
next prev parent reply other threads:[~2024-09-21 10:23 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-03 7:12 bug#72437: Unexpected behavior when inserting with prefix arg in electric-pair-mode Joseph Turner via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-03 9:12 ` Eli Zaretskii
2024-08-03 11:04 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-03 15:40 ` Eli Zaretskii
2024-08-03 18:18 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-03 18:46 ` Eli Zaretskii
2024-08-14 1:00 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-14 5:07 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-14 11:39 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-17 6:28 ` Joseph Turner via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-09-19 5:14 ` Joseph Turner via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-09-21 10:23 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors [this message]
2024-09-21 17:24 ` Joseph Turner via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-09-22 8:21 ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-09-22 19:32 ` Joseph Turner via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-14 0:57 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-16 21:22 ` João Távora
2024-08-17 6:08 ` Eli Zaretskii
2024-08-17 9:30 ` João Távora
2024-08-17 14:03 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-17 14:21 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=m1jzf55mpw.fsf@eshelyaron.com \
--to=bug-gnu-emacs@gnu.org \
--cc=72437@debbugs.gnu.org \
--cc=eliz@gnu.org \
--cc=joseph@breatheoutbreathe.in \
--cc=me@eshelyaron.com \
--cc=monnier@iro.umontreal.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.