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)) ?\]