1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

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.  (Bug#72437)
This commit is contained in:
Eshel Yaron 2024-09-21 12:15:16 +02:00
parent f5bf007df1
commit a7192fd7b7
No known key found for this signature in database
GPG key ID: EF3EE9CA35D78618
4 changed files with 67 additions and 36 deletions

View file

@ -1061,7 +1061,9 @@ one, no insertion takes places, and that position is simply skipped
over. If the region is active (@pxref{Mark}), insertion of a over. If the region is active (@pxref{Mark}), insertion of a
delimiter operates on the region: the characters in the region are delimiter operates on the region: the characters in the region are
enclosed in a pair of matching delimiters, leaving point after the 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: These variables control additional features of Electric Pair mode:

View file

@ -160,6 +160,12 @@ This option, if non-nil, makes 'delete-pair' push a mark at the end of
the region enclosed by the deleted delimiters. This makes it easy to the region enclosed by the deleted delimiters. This makes it easy to
act on that region. For example, we can highlight it using 'C-x C-x'. act on that region. For example, we can highlight it using 'C-x C-x'.
+++
** 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.
* Changes in Specialized Modes and Packages in Emacs 31.1 * Changes in Specialized Modes and Packages in Emacs 31.1

View file

@ -260,7 +260,7 @@ inside a comment or string."
(list ?\( (cdr direct) 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)))))
(defun electric-pair--insert (char) (defun electric-pair--insert (char times)
(let ((last-command-event char) (let ((last-command-event char)
(blink-matching-paren nil) (blink-matching-paren nil)
(electric-pair-mode nil) (electric-pair-mode nil)
@ -271,7 +271,7 @@ inside a comment or string."
;; us to add these newlines, and is probably about to kick in ;; us to add these newlines, and is probably about to kick in
;; again after we add the closer. ;; again after we add the closer.
(electric-layout-allow-duplicate-newlines t)) (electric-layout-allow-duplicate-newlines t))
(self-insert-command 1))) (self-insert-command times)))
(defun electric-pair--syntax-ppss (&optional pos where) (defun electric-pair--syntax-ppss (&optional pos where)
"Like `syntax-ppss', but sometimes fallback to `parse-partial-sexp'. "Like `syntax-ppss', but sometimes fallback to `parse-partial-sexp'.
@ -455,7 +455,8 @@ happened."
(atomic-change-group (atomic-change-group
;; Don't use `delete-char'; that may modify the head of the ;; Don't use `delete-char'; that may modify the head of the
;; undo list. ;; undo list.
(delete-region (point) (1- (point))) (delete-region (- (point) (prefix-numeric-value current-prefix-arg))
(point))
(throw (throw
'done 'done
(cond ((eq ?\( syntax) (cond ((eq ?\( syntax)
@ -474,15 +475,16 @@ happened."
Works by first removing the character from the buffer, then doing Works by first removing the character from the buffer, then doing
some list calculations, finally restoring the situation as if nothing some list calculations, finally restoring the situation as if nothing
happened." happened."
(let ((num (prefix-numeric-value current-prefix-arg)))
(pcase (electric-pair-syntax-info char) (pcase (electric-pair-syntax-info char)
(`(,syntax ,pair ,_ ,s-or-c) (`(,syntax ,pair ,_ ,s-or-c)
(unwind-protect (unwind-protect
(progn (progn
(delete-char -1) (delete-char (- num))
(cond ((eq syntax ?\)) (cond ((eq syntax ?\))
(let* ((pair-data (let* ((pair-data
(electric-pair--balance-info (electric-pair--balance-info
-1 s-or-c)) (- num) s-or-c))
(innermost (car pair-data)) (innermost (car pair-data))
(outermost (cdr pair-data))) (outermost (cdr pair-data)))
(and (and
@ -492,7 +494,7 @@ happened."
(not (eq (cdr outermost) pair))))))) (not (eq (cdr outermost) pair)))))))
((eq syntax ?\") ((eq syntax ?\")
(electric-pair--inside-string-p char)))) (electric-pair--inside-string-p char))))
(insert char))))) (insert (make-string num char)))))))
(defun electric-pair-default-skip-self (char) (defun electric-pair-default-skip-self (char)
(if electric-pair-preserve-balance (if electric-pair-preserve-balance
@ -527,11 +529,14 @@ The decision is taken by order of preference:
`electric-pair-inhibit-predicate', `electric-pair-skip-self' `electric-pair-inhibit-predicate', `electric-pair-skip-self'
and `electric-pair-skip-whitespace' (which see)." and `electric-pair-skip-whitespace' (which see)."
(let* ((pos (and electric-pair-mode (electric--after-char-pos))) (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)) (skip-whitespace-info))
(pcase (electric-pair-syntax-info last-command-event) (pcase (electric-pair-syntax-info last-command-event)
(`(,syntax ,pair ,unconditional ,_) (`(,syntax ,pair ,unconditional ,_)
(cond (cond
((null pos) nil) ((null pos) nil)
((zerop num) nil)
;; Wrap a pair around the active region. ;; Wrap a pair around the active region.
;; ;;
((and (memq syntax '(?\( ?\) ?\" ?\$)) (use-region-p)) ((and (memq syntax '(?\( ?\) ?\" ?\$)) (use-region-p))
@ -545,16 +550,17 @@ The decision is taken by order of preference:
(>= (mark) (point)))) (>= (mark) (point))))
(save-excursion (save-excursion
(goto-char (mark)) (goto-char (mark))
(electric-pair--insert pair)) (electric-pair--insert pair num))
(delete-region pos (1- pos)) (delete-region beg pos)
(electric-pair--insert pair) (electric-pair--insert pair num)
(goto-char (mark)) (goto-char (mark))
(electric-pair--insert last-command-event))) (electric-pair--insert last-command-event num)))
;; Backslash-escaped: no pairing, no skipping. ;; Backslash-escaped: no pairing, no skipping.
((save-excursion ((save-excursion
(goto-char (1- pos)) (goto-char beg)
(not (zerop (% (skip-syntax-backward "\\") 2)))) (not (zerop (% (skip-syntax-backward "\\") 2))))
nil) (let ((current-prefix-arg (1- num)))
(electric-pair-post-self-insert-function)))
;; Skip self. ;; Skip self.
((and (memq syntax '(?\) ?\" ?\$)) ((and (memq syntax '(?\) ?\" ?\$))
(and (or unconditional (and (or unconditional
@ -580,10 +586,10 @@ The decision is taken by order of preference:
;; live with it for now. ;; live with it for now.
(when skip-whitespace-info (when skip-whitespace-info
(funcall electric-pair-skip-whitespace-function)) (funcall electric-pair-skip-whitespace-function))
(delete-region (1- pos) (if (eq skip-whitespace-info 'chomp) (delete-region beg (if (eq skip-whitespace-info 'chomp)
(point) (point)
pos)) pos))
(forward-char)) (forward-char num))
;; Insert matching pair. ;; Insert matching pair.
((and (memq syntax '(?\( ?\" ?\$)) ((and (memq syntax '(?\( ?\" ?\$))
(not overwrite-mode) (not overwrite-mode)
@ -592,7 +598,7 @@ The decision is taken by order of preference:
(goto-char pos) (goto-char pos)
(funcall electric-pair-inhibit-predicate (funcall electric-pair-inhibit-predicate
last-command-event))))) last-command-event)))))
(save-excursion (electric-pair--insert pair)))))))) (save-excursion (electric-pair--insert pair num))))))))
(defun electric-pair-open-newline-between-pairs-psif () (defun electric-pair-open-newline-between-pairs-psif ()
"Honor `electric-pair-open-newline-between-pairs'. "Honor `electric-pair-open-newline-between-pairs'.
@ -604,7 +610,8 @@ Member of `post-self-insert-hook' if `electric-pair-mode' is on."
(< (1+ (point-min)) (point) (point-max)) (< (1+ (point-min)) (point) (point-max))
(eq (save-excursion (eq (save-excursion
(skip-chars-backward "\t\s") (skip-chars-backward "\t\s")
(char-before (1- (point)))) (char-before (- (point)
(prefix-numeric-value current-prefix-arg))))
(matching-paren (char-after)))) (matching-paren (char-after))))
(save-excursion (newline 1 t)))) (save-excursion (newline 1 t))))
@ -618,7 +625,7 @@ Member of `post-self-insert-hook' if `electric-pair-mode' is on."
ARG and KILLP are passed directly to ARG and KILLP are passed directly to
`backward-delete-char-untabify', which see." `backward-delete-char-untabify', which see."
(interactive "*p\nP") (interactive "*p\nP")
(delete-char 1) (delete-char arg)
(backward-delete-char-untabify arg killp)) (backward-delete-char-untabify arg killp))
(defvar electric-pair-mode-map (defvar electric-pair-mode-map

View file

@ -653,6 +653,22 @@ baz\"\""
(skip-chars-backward "\"") (skip-chars-backward "\"")
(mark-sexp -1))) (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)))
;;; Electric quotes ;;; Electric quotes
(define-electric-pair-test electric-quote-string (define-electric-pair-test electric-quote-string