bug#50236: 27.2; electric-pair-mode is inconvenient in comint
João Távora <[email protected]>
| Newsgroups | gmane.emacs.bugs |
|---|---|
| Message-ID | <[email protected]> |
Andrew Hyatt <[email protected]> writes: > Maybe. But it's not now how I see responsible, maintainable > design. In my opinion there are even simpler ways. > > In the effort to get a fix out, I'd agree that we should make the > simple change now (the one I have done), and we can later always > substitute it with something more customizable. I don't think the > customization is necessary for this bugfix. Yes, indeed in the interest of getting this bug fix moving and hopefully into emacs-31, lets: 1. Push the attached patch to emacs-31 (this is a bug fix after all). Give it some testing. I'll be testing it myself. If there are no objections, I'll push in a few days time. BTW I've measured the performance informally: emacs -Q -nw -f electric-pair-mode src/xdisp.c M-g M-g 500 RET ; goto line 500 C-m ; hold C-m for 10 seconds Did this experiment three times withou and without the patch with patch: line reached: 733, 736, 736 %85 max cpu without patch: line reached: 732, 738, 737 %83.2 max cpu Even though these measurements are not concerning at all, I still opted to set the the controlling variable to nil everwhere but in comint-mode, where it is set to 1000. Also I've credited the entire patch to Andrew since using fields is his original idea and added myself as "Co-authored-by:" If there are objections, let me know please before I push it. 2. Let's also keep this bug open (or open a new one) so that we can continue discussing: 2.1 the merits of adding 'field' definitions to org-mode and markdown-mode and other modes 2.2 and/or the merits up upgrading the new controlling variable to something more generic that encompasses other parenthesis-matching modes if this last point goes forward, as Augusto would like, it can be made easily backward compatible to the current scheme, by using the existing electric-pair-field-search-size to be a function that returns two numbers. Currently its semantics aref nil for using the whole of the accessible buffer a number to search that far for fields If we ever do this upgrade, we can just rename the variable. João
0001-Make-electric-pair-mode-respect-field-boundaries-bug.patch
(text/x-patch, 5.7 KB)
From ca8bb2aaff67e3663628eb814800b81b0c1160b4 Mon Sep 17 00:00:00 2001 From: Andrew Hyatt <[email protected]> Date: Sun, 16 Aug 2026 12:31:55 +0100 Subject: [PATCH] Make electric-pair-mode respect field boundaries (bug#50236) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Among other potential benefits, this makes e-p-m work correctly in all comint-based modes. * lisp/elec-pair.el (electric-pair-field-search-size): New variable. (electric-pair-post-self-insert-function): Tweak. (electric--pair-psif-1) * etc/NEWS: Call out change. * lisp/comint.el (electric-pair-field-search-size): forward-declare. (comint-mode): Set electric-pair-field-search-size to 1000. * test/lisp/electric-tests.el (preserve-balance) (preserve-balance-not-across-fields): New tests. Co-authored-by: João Távora <[email protected]> --- etc/NEWS | 7 +++++++ lisp/comint.el | 5 ++++- lisp/elec-pair.el | 24 +++++++++++++++++++++++- test/lisp/electric-tests.el | 26 +++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 5f5f59d2a9f..fe662e4421d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -972,6 +972,13 @@ To use this, add a list to both electric pair user options: '("/*" . "*/")'. You can also specify that an extra space should be inserted after the first string, like this: '("/*" " */" t)'. +*** Electric pair mode can restrict operation to nearby fields +This is particularly useful in comint-based modes (like Eshell) and +other situations where process output may contain unpaired delimiters, +while keeping delimiters you insert balanced. The +''electric-pair-field-search-size'' variable controls the search +distance for nearby fields. + --- ** New user option 'electric-indent-actions'. This user option specifies a list of actions to reindent. The possible diff --git a/lisp/comint.el b/lisp/comint.el index e5418f4ee67..3018717c937 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -702,6 +702,8 @@ comint-stored-incomplete-input (put 'comint-mode 'mode-class 'special) +(defvar electric-pair-field-search-size) + (define-derived-mode comint-mode fundamental-mode "Comint" "Major mode for interacting with an inferior interpreter. Interpreter name is same as buffer name, sans the asterisks. @@ -792,7 +794,8 @@ comint-mode (add-hook 'isearch-mode-hook #'comint-history-isearch-setup nil t) (add-hook 'completion-at-point-functions #'comint-completion-at-point nil t) ;; This behavior is not useful in comint buffers, and is annoying - (setq-local next-line-add-newlines nil)) + (setq-local next-line-add-newlines nil) + (setq electric-pair-field-search-size 1000)) (defun comint-check-proc (buffer) "Return non-nil if there is a living process associated w/buffer BUFFER. diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el index 54fbc960b0f..d7ae555e408 100644 --- a/lisp/elec-pair.el +++ b/lisp/elec-pair.el @@ -596,10 +596,32 @@ electric-pair-default-inhibit (electric-pair-inhibit-if-helps-balance char) (electric-pair-conservative-inhibit char))) +(defvar electric-pair-field-search-size nil + "How far to look for nearby fields to restrict `electric-pair-mode'. + +If nil, do not look for nearby fields and use the accessible portion of +the buffer. If a integer number of characters, look at most that far +backward and forward for a field boundary. If one isn't found, use the +accessible portion of the buffer in that direction.") + (defun electric-pair-post-self-insert-function () "Do main work for `electric-pair-mode'. This function is added to `post-self-insert-hook' when -`electric-pair-mode' is enabled. +`electric-pair-mode' is enabled." + ;; First, figure out whether to restrit the buffer (bug#50236) + (if (or (null electric-pair-field-search-size) + (use-region-p)) + (electric--pair-psif-1) + (let* ((min (- (point) electric-pair-field-search-size)) + (max (+ (point) electric-pair-field-search-size)) + (beg (field-beginning nil nil (max (point-min) min))) + (end (field-end nil nil (min (point-max) max)))) + (with-restriction + (if (= min beg) (point-min) beg) (if (= max end) (point-max) end) + (electric--pair-psif-1))))) + +(defun electric--pair-psif-1 () + "Do main work for `electric-pair-post-self-insert-function.' If the newly inserted character C has delimiter syntax, this function may decide to insert additional paired delimiters, or diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el index 9209e1739fd..d678279c8cc 100644 --- a/test/lisp/electric-tests.el +++ b/test/lisp/electric-tests.el @@ -703,7 +703,31 @@ autowrapping-multi-2 (goto-char (point-max)) (skip-chars-backward "\"") (mark-sexp -1))) - + +(define-electric-pair-test preserve-balance + "\"\n\n" "---\"" + :expected-string "\"\n\n\"" + :expected-point 5 + :modes '(fundamental-mode) + :test-in-comments nil + :test-in-strings nil + :bindings '((electric-pair-preserve-balance . t)) + :fixture-fn (lambda () + (electric-pair-mode 1))) + +(define-electric-pair-test preserve-balance-not-across-fields + "\"\n\n" "---\"" + :expected-string "\"\n\n\"\"" + :expected-point 5 + :modes '(fundamental-mode) + :test-in-comments nil + :test-in-strings nil + :bindings '((electric-pair-preserve-balance . t) + (electric-pair-field-search-size . 100)) + :fixture-fn (lambda () + (electric-pair-mode 1) + (add-text-properties (point-min) (+ (point-min) 2) + '(field t)))) ;;; Electric quotes (define-electric-pair-test electric-quote-string -- 2.54.0