scratch/undo-apply d8aeff97494 2/3: Improve undo-in-region support for diff-unified->context

Helmut Eller <[email protected]> Sun, 26 Jul 2026 12:56:32 -0400 (EDT)
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: scratch/undo-apply
commit d8aeff97494d80ebdaf48a2ca4a83bb1fbd705ad
Author: Helmut Eller <[email protected]>
Commit: Helmut Eller <[email protected]>

    Improve undo-in-region support for diff-unified->context
    
    * lisp/vc/diff-mode.el (diff-unified->context, diff-context->unified):
    Use the new 'apply variant for buffer-undo-list entries.
---
 lisp/vc/diff-mode.el            |  10 ++--
 test/lisp/vc/diff-mode-tests.el | 115 +++++++++++++++++++++++++++++++++++-----
 2 files changed, 109 insertions(+), 16 deletions(-)

diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index c27abda1af9..6db3d4bb803 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -1399,8 +1399,9 @@ else cover the whole buffer."
                   ;; a single entry that uses diff-context->unified to do
                   ;; the work.
 		  (setq buffer-undo-list
-			(cons (list 'apply (- old-end end) start (point-max)
-				    'diff-context->unified start (point-max))
+			(cons (list 'apply (- old-end end)
+                                    (cons start (point-max))
+                                    'diff-context->unified)
 			      old-undo)))))))))))
 
 (defun diff-context->unified (start end &optional to-context)
@@ -1497,8 +1498,9 @@ With a prefix argument, convert unified format to context format."
                   ;; of undo elements with just a single special one.
                   (unless (or (not reversible) (eq buffer-undo-list t))
                     (setq buffer-undo-list
-                          (cons (list 'apply (- old-end end) pt1 (point)
-                                      'diff-unified->context pt1 (point))
+                          (cons (list 'apply (- old-end end)
+                                      (cons pt1 (point))
+                                      'diff-unified->context)
                                 old-undo)))
                   )))))))))
 
diff --git a/test/lisp/vc/diff-mode-tests.el b/test/lisp/vc/diff-mode-tests.el
index 875f5cf8bfe..13cb81eaa10 100644
--- a/test/lisp/vc/diff-mode-tests.el
+++ b/test/lisp/vc/diff-mode-tests.el
@@ -846,20 +846,111 @@ index 0000000..3456789
 ")
 
 (ert-deftest diff-mode-tests-undo-unified->context ()
+  (let* ((unified diff-mode-tests--unified-patch)
+         (context diff-mode-tests--context-patch))
+    (pcase-dolist (`(,convert ,v1 ,v2)
+                   `((,#'diff-unified->context ,unified ,context)
+                     (,#'diff-context->unified ,context ,unified)))
+      (let ((b (generate-new-buffer "test.diff")))
+        (unwind-protect
+	    (with-current-buffer b
+	      (insert v1)
+	      (diff-mode)
+	      (undo-boundary)
+	      (funcall convert (point-min) (point-max))
+              (should (equal (buffer-string) v2))
+	      (undo-boundary)
+	      (undo)
+	      (should (equal (buffer-string) v1)))
+          (kill-buffer b))))))
+
+(defvar diff-mode-tests--selective-undo-patch-1
+  "--- /tmp/a.el	2026-07-14 08:27:52.751202012 +0200
++++ /tmp/b.el	2026-07-14 08:28:01.087191220 +0200
+@@ -2,7 +2,7 @@ (defun foo ()
+   x
+   y
+   z
+-  a)
++  b)
+ ;;
+ ;;
+ ;;
+*************** (defun bar ()
+*** 19,22 ****
+    x
+    y
+    z
+!   aa)
+--- 19,22 ----
+    x
+    y
+    z
+!   bb)
+")
+
+(defvar diff-mode-tests--selective-undo-patch-2
+  "*** /tmp/a.el	2026-07-14 08:27:52.751202012 +0200
+--- /tmp/b.el	2026-07-14 08:28:01.087191220 +0200
+*************** (defun foo ()
+*** 2,8 ****
+    x
+    y
+    z
+!   a)
+  ;;
+  ;;
+  ;;
+--- 2,8 ----
+    x
+    y
+    z
+!   b)
+  ;;
+  ;;
+  ;;
+@@ -19,4 +19,4 @@ (defun bar ()
+   x
+   y
+   z
+-  aa)
++  bb)
+")
+
+(defun diff-mode-tests--mark-region (beg end)
+  (transient-mark-mode 1)
+  (goto-char beg)
+  (push-mark (point) t t)
+  (setq mark-active t)
+  (goto-char end))
+
+(ert-deftest diff-mode-tests-selective-undo ()
   (let* ((unified diff-mode-tests--unified-patch)
          (context diff-mode-tests--context-patch)
-	 (b (generate-new-buffer "test.diff")))
-    (unwind-protect
-	(with-current-buffer b
-	  (insert unified)
-	  (diff-mode)
-	  (undo-boundary)
-	  (diff-unified->context (point-min) (point-max))
-          (should (equal (buffer-string) context))
-	  (undo-boundary)
-	  (undo)
-	  (should (equal (buffer-string) unified)))
-      (kill-buffer b))))
+         (mixed-1 diff-mode-tests--selective-undo-patch-1)
+         (mixed-2 diff-mode-tests--selective-undo-patch-2))
+    (pcase-dolist (`(,convert ,v1 ,v2 ,v3)
+                   `((,#'diff-unified->context ,unified ,context ,mixed-1)
+                     (,#'diff-context->unified ,context ,unified ,mixed-2)))
+      (let ((b (generate-new-buffer "test.diff")))
+        (unwind-protect
+	    (with-current-buffer b
+	      (insert v1)
+	      (diff-mode)
+	      (undo-boundary)
+	      (funcall convert (point-min) (point-max))
+              (should (equal (buffer-string) v2))
+              (goto-char (point-min))
+              (re-search-forward "(defun foo ()")
+              (replace-match "(defun baz ()")
+              (goto-char (point-min))
+              (should (not (re-search-forward "(defun foo ()" nil t)))
+              (re-search-forward (regexp-quote "(defun bar ()"))
+              (diff-mode-tests--mark-region (point-min) (point))
+	      (undo-boundary)
+	      (undo)
+              (should (equal (buffer-string) v3)))
+          (kill-buffer b))))))
 
 (provide 'diff-mode-tests)
 ;;; diff-mode-tests.el ends here