branch: externals/vundo
commit e76b773b5d8ca775bba94feba866ee3a49ffbe82
Author: Paul Nelson <[email protected]>
Commit: Yuan Fu <[email protected]>

    vundo-diff: add validity check
    
    Fixes issues similar to #122, but for vundo-diff.
    
    * vundo-diff.el (vundo-diff--node-valid-p): New function.
    (vundo-diff): Use it to check whether marked node is still valid.
    
    * test/vundo-test.el (vundo)
    (vundo-test--diff-refresh-stale-marked-node)
    (vundo-test--diff-stale-marked-node-no-possible-route): New tests.
---
 test/vundo-test.el | 46 +++++++++++++++++++++++++++++++++++++
 vundo-diff.el      | 67 ++++++++++++++++++++++++++++++++----------------------
 2 files changed, 86 insertions(+), 27 deletions(-)

diff --git a/test/vundo-test.el b/test/vundo-test.el
index cb7961f4d5..03eb1eb934 100644
--- a/test/vundo-test.el
+++ b/test/vundo-test.el
@@ -10,6 +10,7 @@
 
 (require 'ert)
 (require 'vundo)
+(require 'vundo-diff)
 (require 'subr-x)
 (require 'cl-lib)
 
@@ -272,6 +273,51 @@ Sans ending newline."
        (undo-boundary))
      (should (equal (vundo-goto-last-saved 1) "Refresh")))))
 
+(ert-deftest vundo-test--diff-refresh-stale-marked-node ()
+  "`vundo-diff' should refresh and not produce a diff on stale vundo buffer."
+  (vundo-test--setup
+   (insert "A") (undo-boundary)
+   (insert "B") (undo-boundary)
+   (with-current-buffer (vundo-1 (current-buffer))
+     (let* ((oname (buffer-name vundo--orig-buffer))
+            (diff-buf-name (concat "*vundo-diff-" oname "*")))
+       ;; Mark tip (B), move back to (A).
+       (vundo-diff-mark)
+       (vundo-backward 1)
+       ;; Make vundo buffer stale.
+       (with-current-buffer vundo--orig-buffer
+         (push (cons t (current-time)) buffer-undo-list))
+       ;; Ensure we start clean.
+       (when-let ((buf (get-buffer diff-buf-name)))
+         (kill-buffer buf))
+       (should-not (get-buffer diff-buf-name))
+       ;; Make sure entire command is skipped after refresh.
+       (should (equal (vundo-diff) "Refresh"))
+       (should-not (get-buffer diff-buf-name))))))
+
+(ert-deftest vundo-test--diff-stale-marked-node-no-possible-route ()
+  "`vundo-diff' should tolerate stale marked nodes."
+  (vundo-test--setup
+   (insert "A") (undo-boundary)
+   (insert "B") (undo-boundary)
+   (with-current-buffer (vundo-1 (current-buffer))
+     ;; Make vundo buffer stale by editing the original buffer.
+     (with-current-buffer vundo--orig-buffer
+       (let ((inhibit-read-only t))
+         (setq-local buffer-read-only nil)
+         (goto-char (point-max))
+         (insert "C")
+         (undo-boundary)))
+     ;; Mark while stale.
+     (vundo-diff-mark)
+     ;; Run a normal vundo command to trigger refresh.
+     (should (equal (vundo-backward 1) "Refresh"))
+     ;; Should not error.  Regression test -- previously,
+     ;; vundo signalled (error "No possible route").
+     (should-not (condition-case err
+                     (progn (vundo-diff) nil)
+                   (error err))))))
+
 (provide 'vundo-test)
 
 ;;; vundo-test.el ends here
diff --git a/vundo-diff.el b/vundo-diff.el
index a799945f1b..e82b89e498 100644
--- a/vundo-diff.el
+++ b/vundo-diff.el
@@ -139,6 +139,15 @@ NODE defaults to the current node."
       (when (and buf kill) (kill-buffer buf))))
   (remove-hook 'vundo-post-exit-hook #'vundo-diff--quit))
 
+(defun vundo-diff--node-valid-p (node mod-list)
+  "Return non-nil if NODE belongs to MOD-LIST."
+  (and node
+       (let ((idx (vundo-m-idx node)))
+         (and (integerp idx)
+              (<= 0 idx)
+              (< idx (length mod-list))
+              (eq (aref mod-list idx) node)))))
+
 ;;;###autoload
 (defun vundo-diff ()
   "Perform diff between marked and current buffer state.
@@ -148,39 +157,43 @@ the original buffer name."
   ;; We can’t add this hook locally, because the hook runs in the
   ;; original buffer.
   (add-hook 'vundo-post-exit-hook #'vundo-diff--quit 0)
-  (let* ((orig vundo--orig-buffer)
-         (oname (buffer-name orig))
-         (current (vundo--current-node vundo--prev-mod-list))
-         (marked (or vundo-diff--marked-node (vundo-m-parent current)))
-         swapped
-         mrkbuf)
-    (if (or (not current) (not marked) (eq current marked))
-        (message "vundo diff not available.")
-      (setq swapped (> (vundo-m-idx marked) (vundo-m-idx current)))
-      (setq mrkbuf (get-buffer-create
-                    (make-temp-name (concat oname "-vundo-diff-marked"))))
-      (unwind-protect
-          (progn
-            (vundo--check-for-command
+  (vundo--check-for-command
+   (let* ((orig vundo--orig-buffer)
+          (oname (buffer-name orig))
+          (current (vundo--current-node vundo--prev-mod-list))
+          (marked (if (vundo-diff--node-valid-p vundo-diff--marked-node
+                                                vundo--prev-mod-list)
+                      vundo-diff--marked-node
+                    (vundo-diff-unmark)
+                    (and current (vundo-m-parent current))))
+          swapped
+          mrkbuf)
+     (if (or (not current) (not marked) (eq current marked))
+         (message "vundo diff not available.")
+       (setq swapped (> (vundo-m-idx marked) (vundo-m-idx current)))
+       (setq mrkbuf (get-buffer-create
+                     (make-temp-name (concat oname "-vundo-diff-marked"))))
+       (unwind-protect
+           (progn
              (vundo--move-to-node current marked orig vundo--prev-mod-list)
              (with-current-buffer mrkbuf
                (insert-buffer-substring-no-properties orig))
              (vundo--refresh-buffer orig (current-buffer) 'incremental)
              (vundo--move-to-node marked current orig vundo--prev-mod-list)
              (vundo--trim-undo-list orig current vundo--prev-mod-list)
-             (vundo--refresh-buffer orig (current-buffer) 'incremental))
-            (let* ((diff-use-labels nil) ; We let our cleanup handle this.
-                   (a (if swapped current marked))
-                   (b (if swapped marked current))
-                   (abuf (if swapped orig mrkbuf))
-                   (bbuf (if swapped mrkbuf orig))
-                   (dbuf (diff-no-select
-                          abuf bbuf nil t
-                          (get-buffer-create
-                           (concat "*vundo-diff-" oname "*")))))
-              (vundo-diff--cleanup-diff-buffer oname dbuf current a b)
-              (display-buffer dbuf)))
-        (kill-buffer mrkbuf)))))
+             (vundo--refresh-buffer orig (current-buffer) 'incremental)
+             (let* ((diff-use-labels nil) ; We let our cleanup handle this.
+                    (a (if swapped current marked))
+                    (b (if swapped marked current))
+                    (abuf (if swapped orig mrkbuf))
+                    (bbuf (if swapped mrkbuf orig))
+                    (dbuf (diff-no-select
+                           abuf bbuf nil t
+                           (get-buffer-create
+                            (concat "*vundo-diff-" oname "*")))))
+               (vundo-diff--cleanup-diff-buffer oname dbuf current a b)
+               (display-buffer dbuf)))
+         (kill-buffer mrkbuf))))))
 
 (defconst vundo-diff-font-lock-keywords
   `((,(rx bol (or "---" "+++") (* nonl) "[mod " (group (+ num)) ?\]

Reply via email to