master 8a8d9b5c6a4: Make electric-pair-mode respect field boundaries (bug#50236)

João Távora <[email protected]>
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: master
commit 8a8d9b5c6a48239b235c9cf5bc4e8816e3520501
Author: Andrew Hyatt <[email protected]>
Commit: João Távora <[email protected]>

    Make electric-pair-mode respect field boundaries (bug#50236)
    
    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, call
    electric--pair-psif-1 (electric--pair-psif-1): New helper from old
    electric-pair-post-self-insert-function.
    
    * 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                    |  9 +++++++++
 lisp/comint.el              |  5 ++++-
 lisp/elec-pair.el           | 24 +++++++++++++++++++++++-
 test/lisp/electric-tests.el | 26 +++++++++++++++++++++++++-
 4 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index d9c30749ab4..9a88a6d4403 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -120,6 +120,15 @@ look after the replacement, using the new face 'query-replace-preview'.
 This tells you what back-references like '\1' expand to before you
 commit to the edit.  The preview is off by default.
 
+** Electric Pair mode
+
+*** Electric pair mode can restrict operation to nearby fields
+This is particularly useful in comint-based modes (like 'M-x shell' or
+'M-x run-python') 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.
+
 
 * Changes in Specialized Modes and Packages in Emacs 32.1
 
diff --git a/lisp/comint.el b/lisp/comint.el
index e5418f4ee67..a78002ab6b8 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -702,6 +702,8 @@ The command \\[comint-accumulate] sets this.")
 
 (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 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
   (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-local 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..700dae0a41c 100644
--- a/lisp/elec-pair.el
+++ b/lisp/elec-pair.el
@@ -596,10 +596,32 @@ happened."
       (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, falling back to 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 @@ baz\"\""
                 (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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.