branch: externals/vundo
commit 06de574d4f9228c819e5444e5da84695941e9953
Author: Yuan Fu <caso...@gmail.com>
Commit: Yuan Fu <caso...@gmail.com>

    Ignore position-only records when generating mod-list
    
    * vundo.el (vundo--position-only-p): New function.
    (vundo--mod-list-from): Add a check before adding a new vundo-m.
---
 vundo.el | 45 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 41 insertions(+), 4 deletions(-)

diff --git a/vundo.el b/vundo.el
index 872c4ac34e..bed630736c 100644
--- a/vundo.el
+++ b/vundo.el
@@ -65,6 +65,25 @@
 ;;
 ;; For a high-level explanation of how this package works, see
 ;; https://archive.casouri.cat/note/2021/visual-undo-tree.
+;;
+;; Position-only records
+;;
+;; We know how undo works: when undoing, ‘primitive-undo’ looks at
+;; each record in ‘pending-undo-list’ and modify the buffer
+;; accordingly, and that modification itself pushes new undo records
+;; into ‘buffer-undo-list’. However, not all undo records introduce
+;; modification, if the record is an integer, ‘primitive-undo’ simply
+;; ‘goto’ that position, which introduces no modification to the
+;; buffer and pushes no undo record to ‘buffer-undo-list’. Normally
+;; position records accompany other buffer-modifying records, but if a
+;; particular record consist of only position records, we have
+;; trouble: after an undo step, ‘buffer-undo-list’ didn’t grow, as far
+;; as vundo tree-folding algorithm is concerned, we didn’t move.
+;; Assertions expecting to see new undo records in ‘buffer-undo-list’
+;; are also violated. To avoid all these complications, we ignore
+;; position-only records when generating mod-list in
+;; ‘vundo--mod-list-from’. These records are not removed, but they
+;; can’t harm us now.
 
 ;;; Code:
 
@@ -204,6 +223,19 @@ modification."
    :type integer
    :documentation "Marks the text node in the vundo buffer if drawn."))
 
+(defsubst vundo--position-only-p (idx undo-list)
+  "Check if the records at IDX in UNDO-LIST is position-only.
+Position-only means all records from IDX to the next undo
+boundary are position records. Position record is just an
+integer (see ‘buffer-undo-list’). Assumes the beginning of the
+UNDO-LIST is not nil."
+  (catch 'ret
+    (while (nth idx undo-list)
+      (when (not (integerp (nth idx undo-list)))
+        (throw 'ret nil))
+      (cl-incf idx))
+    t))
+
 (defun vundo--mod-list-from (undo-list &optional n mod-list)
   "Generate and return a modification list from UNDO-LIST.
 If N non-nil, only look at the first N entries in UNDO-LIST.
@@ -217,10 +249,15 @@ If MOD-LIST non-nil, extend on MOD-LIST."
       (while (and (< uidx bound) (null (nth uidx undo-list)))
         (cl-incf uidx))
       ;; Add modification.
-      (when (< uidx bound)
-        (cl-assert (not (null (nth uidx undo-list))))
-        (push (make-vundo-m :undo-list (nthcdr uidx undo-list))
-              new-mlist))
+      (unless (vundo--position-only-p uidx undo-list)
+        ;; If this record is position-only, we skip it and don’t add a
+        ;; mod for it. Effectively taking it out of the undo tree.
+        ;; Read ‘Position-only records’ section in Commentary for more
+        ;; explanation.
+        (when (< uidx bound)
+          (cl-assert (not (null (nth uidx undo-list))))
+          (push (make-vundo-m :undo-list (nthcdr uidx undo-list))
+                new-mlist)))
       ;; Skip through the content of this modification.
       (while (nth uidx undo-list)
         (cl-incf uidx)))

Reply via email to