Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
undo-tree: Create a separate copy of `buffer-undo-tree` in Emacs 26.0.50
Q: Using Emacs 26.0.50, how to copy the buffer-undo-tree preserving the structure and create a completely separate copy -- including the hash-table (aka object-pool)?
When using Emacs 25.2.1, setting the hash-table of the copy of buffer-undo-tree to a nil value does not alter the original buffer-undo-tree.
When using Emacs 26.0.50 (master branch), setting the hash-table of the copy of buffer-undo-tree to a nil value alters the original buffer-undo-tree -- setting it to nil.
A wild guess is that setf or cl-defstruct may have undergone improvements [?] in the master branch that have lead to this new problem.
STEP 1: Download the latest version of undo-tree.el from the emacs mirror.
STEP 2: Extract undo-tree.el from the zipped archive that was downloaded in the first step.
STEP 3: Launch Emacs without any user configuration; i.e., emacs -q.
STEP 4: Open undo-tree.el with Emacs and evaluate the buffer; e.g., M-x eval-buffer.
STEP 5: Switch to the *scratch* buffer, paste the structure preserving copy-tree*... functions written by @Tobias, and evaluate the buffer; e.g., M-x eval-buffer.
(require 'cl)
;;; Written by @Tobias:
(cl-defstruct (copy-tree*
(:constructor copy-tree*-mem
(&optional stack stack-new (hash (make-hash-table)))))
stack stack-new hash)
(defmacro copy-tree*--push (el el-new mem &optional hash)
"Written by @Tobias:"
(let ((my-el (make-symbol "my-el"))
;; makes sure `el' is only evaluated once
(my-el-new (make-symbol "my-el-new")))
(append `(let ((,my-el ,el)
(,my-el-new ,el-new))
(push ,my-el (copy-tree*-stack ,mem))
(push ,my-el-new (copy-tree*-stack-new ,mem)))
(and hash
`((puthash ,my-el ,my-el-new (copy-tree*-hash ,mem))))
(list my-el-new))))
(defmacro copy-tree*--pop (el el-new mem)
"Written by @Tobias:"
`(setq ,el (pop (copy-tree*-stack ,mem))
,el-new (pop (copy-tree*-stack-new mem))))
(defun copy-tree*--copy-node (node mem vecp)
"Written by @Tobias:"
(if (or (consp node)
(and vecp (vectorp node)))
(let ((existing-node (gethash node (copy-tree*-hash mem))))
(if existing-node
(copy-tree*--push node (if (consp node)
(cons nil nil)
(make-vector (length node) nil))
mem t)))
(defun copy-tree* (tree &optional vecp)
"Written by @Tobias:"
(if (or (consp tree)
(and vecp (vectorp tree)))
(let* ((tree-new (if (consp tree) (cons nil nil)
(make-vector (length tree) nil)))
(mem (copy-tree*-mem))
(copy-tree*--push tree tree-new mem t)
(while (copy-tree*--pop next next-new mem)
((consp next)
(setcar next-new (copy-tree*--copy-node (car next) mem vecp))
(setcdr next-new (copy-tree*--copy-node (cdr next) mem vecp)))
((and vecp (vectorp next))
(cl-loop for i from 0 below (length next) do
(aset next-new i
(copy-tree*--copy-node (aref next i) mem vecp))))))
STEP 6: Evaluate (undo-list-transfer-to-tree)
STEP 7: Evaluate (setq foo (copy-tree* buffer-undo-tree 'vecp))
STEP 8: Evaluate (setf (undo-tree-object-pool foo) nil)
STEP 9: Evaluate (undo-tree-object-pool buffer-undo-tree)
Possibly relevant: structs are now records in Emacs 26, before they were just vectors
with a special symbol in the first element. – `npostavs` Jul 20 '17 at 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.