[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/undo-tree 7171865096 8/9: Merge remote-tracking branch
From: |
Stefan Monnier |
Subject: |
[elpa] externals/undo-tree 7171865096 8/9: Merge remote-tracking branch 'upstream/undo-tree/main' into externals/undo-tree |
Date: |
Sat, 12 Mar 2022 18:02:56 -0500 (EST) |
branch: externals/undo-tree
commit 71718650961b8546a3cba311c6cc8e069c85e882
Merge: bf2e9ba0c9 2bf5e230f1
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>
Merge remote-tracking branch 'upstream/undo-tree/main' into
externals/undo-tree
---
undo-tree.el | 339 +++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 225 insertions(+), 114 deletions(-)
diff --git a/undo-tree.el b/undo-tree.el
index e3785877b1..1b8eb2f8d4 100644
--- a/undo-tree.el
+++ b/undo-tree.el
@@ -1,13 +1,14 @@
;;; undo-tree.el --- Treat undo history as a tree -*- lexical-binding: t; -*-
-;; Copyright (C) 2009-2020 Free Software Foundation, Inc
+;; Copyright (C) 2009-2022 Free Software Foundation, Inc
-;; Author: Toby Cubitt <toby-undo-tree@dr-qubit.org>
+;; Author: Toby Cubitt <toby+undo-tree@dr-qubit.org>
;; Maintainer: Toby Cubitt <toby-undo-tree@dr-qubit.org>
-;; Version: 0.7.5
+;; Version: 0.8.2
;; Keywords: convenience, files, undo, redo, history, tree
-;; URL: http://www.dr-qubit.org/emacs.php
-;; Repository: http://www.dr-qubit.org/git/undo-tree.git
+;; Package-Requires: ((queue "0.2"))
+;; URL: https://www.dr-qubit.org/undo-tree.html
+;; Repository: https://gitlab.com/tsc25/undo-tree
;; This file is part of Emacs.
;;
@@ -755,6 +756,7 @@
;;; Code:
(require 'cl-lib)
+(require 'queue)
(require 'diff)
(require 'gv)
@@ -939,7 +941,7 @@ within the current region."
:type 'boolean)
-(defcustom undo-tree-auto-save-history nil
+(defcustom undo-tree-auto-save-history t
"When non-nil, `undo-tree-mode' will save undo history to file
when a buffer is saved to file.
@@ -1340,6 +1342,26 @@ in visualizer."
(timestamp (current-time))
(branch 0)))
(:constructor undo-tree-make-empty-node ())
+ (:constructor undo-tree-copy-node-save-data
+ (node
+ &aux
+ (undo (let ((changeset (undo-tree-node-undo node)))
+ (run-hook-wrapped
+ 'undo-tree-pre-save-element-functions
+ (lambda (fun)
+ (setq changeset (delq nil (mapcar fun changeset)))
+ nil))
+ changeset))
+ (redo (let ((changeset (undo-tree-node-redo node)))
+ (run-hook-wrapped
+ 'undo-tree-pre-save-element-functions
+ (lambda (fun)
+ (setq changeset (delq nil (mapcar fun changeset)))
+ nil))
+ changeset))
+ (timestamp (undo-tree-node-timestamp node))
+ (branch (undo-tree-node-branch node))
+ (meta-data (undo-tree-node-meta-data node))))
(:copier nil))
previous next undo redo timestamp branch meta-data)
@@ -1349,7 +1371,7 @@ in visualizer."
`(and (vectorp ,n) (= (length ,n) ,len))))
(defun undo-tree-node-copy (node &optional tree current)
- ;; Return a copy of undo-tree NODE, sans previous link or meta-data.
+ ;; Return a deep copy of undo-tree NODE, sans previous link or meta-data.
;; If TREE and CURRENT are supplied, set (undo-tree-current TREE) to the
;; copy of CURRENT node, if found.
(let* ((new (undo-tree-make-empty-node))
@@ -1825,7 +1847,7 @@ Comparison is done with `eq'."
(while undo-tree-gc-flag
(setq undo-tree-gc-flag nil
undo-list (copy-tree buffer-undo-list)))
- (setq buffer-undo-list '(nil undo-tree-canary))
+ (setq buffer-undo-list (list nil 'undo-tree-canary))
;; create new node from first changeset in `undo-list', save old
;; `buffer-undo-tree' current node, and make new node the current node
@@ -1859,8 +1881,7 @@ Comparison is done with `eq'."
(setq node (undo-tree-grow-backwards node nil))
(setf (undo-tree-root buffer-undo-tree) node)
(setf (undo-tree-size buffer-undo-tree) size)
- (setf (undo-tree-count buffer-undo-tree) count)
- (setq undo-list '(nil undo-tree-canary))))))
+ (setf (undo-tree-count buffer-undo-tree) count)))))
;; discard undo history if necessary
(undo-tree-discard-history))
@@ -2205,16 +2226,14 @@ which is defined in the `warnings' library.\n")
;; Return non-nil if NODE corresponds to a buffer state that once upon a
;; time was unmodified. If a file modification time MTIME is specified,
;; return non-nil if the corresponding buffer state really is unmodified.
- (let (changeset ntime)
- (setq changeset
+ (let* ((changeset
(or (undo-tree-node-redo node)
(and (setq changeset (car (undo-tree-node-next node)))
- (undo-tree-node-undo changeset)))
- ntime
- (catch 'found
- (dolist (elt changeset)
- (when (and (consp elt) (eq (car elt) t) (consp (cdr elt))
- (throw 'found (cdr elt)))))))
+ (undo-tree-node-undo changeset))))
+ (ntime
+ (let ((elt (car (last changeset))))
+ (and (consp elt) (eq (car elt) t) (consp (cdr elt))
+ (cdr elt)))))
(and ntime
(or (null mtime)
;; high-precision timestamps
@@ -3217,6 +3236,10 @@ Argument is a character, naming the register."
;;; =====================================================================
;;; Persistent storage commands
+(defvar undo-tree-save-format-version 1
+ "Undo-tree history file format version.")
+
+
(defun undo-tree-make-history-save-file-name (file)
"Create the undo history file name for FILE.
Normally this is the file's name with \".\" prepended and
@@ -3231,6 +3254,111 @@ directory for the backup doesn't exist, it is created."
".~undo-tree~")))
+(defun undo-tree-serialize (tree)
+ "Serialise undo-tree TREE to current buffer."
+ ;; write root
+ (let ((data (undo-tree-copy-node-save-data (undo-tree-root tree))))
+ (when (eq (undo-tree-root tree) (undo-tree-current tree))
+ (setf (undo-tree-node-next data) 'current))
+ (prin1 data (current-buffer)))
+ (terpri (current-buffer))
+ ;; Note: We serialise in breadth-first order, as undo-trees are typically
+ ;; much deeper than they are wide, so this is more memory-efficient.
+ (let ((queue (make-queue)))
+ (queue-enqueue queue (undo-tree-root tree))
+ (while (not (queue-empty queue))
+ (prin1 (mapcar
+ (lambda (n)
+ (queue-enqueue queue n)
+ (let ((data (undo-tree-copy-node-save-data n)))
+ ;; use empty next field to mark current node
+ (when (eq n (undo-tree-current tree))
+ (setf (undo-tree-node-next data) 'current))
+ data))
+ (undo-tree-node-next (queue-dequeue queue)))
+ (current-buffer))
+ (terpri (current-buffer)))))
+
+
+(defun undo-tree-deserialize ()
+ "Deserialize and return undo-tree from current buffer."
+ (let ((tree (make-undo-tree))
+ (queue (make-queue))
+ node)
+ ;; read root
+ (setf (undo-tree-root tree) (read (current-buffer)))
+ (queue-enqueue queue (undo-tree-root tree))
+ ;; reconstruct tree in breadth-first order
+ (while (not (queue-empty queue))
+ (setq node (queue-dequeue queue))
+ (when (eq (undo-tree-node-next node) 'current)
+ (setf (undo-tree-current tree) node))
+ (setf (undo-tree-node-next node) (read (current-buffer)))
+ (mapc (lambda (n) (queue-enqueue queue n))
+ (undo-tree-node-next node)))
+ ;; restore parent links
+ (undo-tree-recircle tree)
+ tree))
+
+
+(defun undo-tree-serialize-old-format (tree)
+ ;; make tmp copy of TREE
+ (setq tree (undo-tree-copy tree))
+ ;; decircle and discard object pool before saving
+ (undo-tree-decircle tree)
+ (setf (undo-tree-object-pool tree) nil)
+ ;; run pre-save transformer functions
+ (when undo-tree-pre-save-element-functions
+ (undo-tree-mapc
+ (lambda (node)
+ (let ((changeset (undo-tree-node-undo node)))
+ (run-hook-wrapped
+ 'undo-tree-pre-save-element-functions
+ (lambda (fun)
+ (setq changeset (delq nil (mapcar fun changeset)))
+ nil))
+ (setf (undo-tree-node-undo node) changeset))
+ (let ((changeset (undo-tree-node-redo node)))
+ (run-hook-wrapped
+ 'undo-tree-pre-save-element-functions
+ (lambda (fun)
+ (setq changeset (delq nil (mapcar fun changeset)))
+ nil))
+ (setf (undo-tree-node-redo node) changeset)))
+ (undo-tree-root tree)))
+ ;; write tree
+ (let ((print-circle t)) (prin1 tree (current-buffer))))
+
+
+(defun undo-tree-deserialize-old-format ()
+ ;; read tree
+ (let ((tree (read (current-buffer))))
+ ;; run post-load transformer functions
+ (when undo-tree-post-load-element-functions
+ (undo-tree-mapc
+ (lambda (node)
+ (let ((changeset (undo-tree-node-undo node)))
+ (run-hook-wrapped
+ 'undo-tree-post-load-element-functions
+ (lambda (fun)
+ (setq changeset (delq nil (mapcar fun changeset)))))
+ (setf (undo-tree-node-undo node) changeset))
+ (let ((changeset (undo-tree-node-redo node)))
+ (run-hook-wrapped
+ 'undo-tree-post-load-element-functions
+ (lambda (fun)
+ (setq changeset (delq nil (mapcar fun changeset)))))
+ (setf (undo-tree-node-redo node) changeset)))
+ (undo-tree-root tree)))
+ ;; initialise empty undo-tree object pool
+ (setf (undo-tree-object-pool tree)
+ (make-hash-table :test 'eq :weakness 'value))
+ ;; restore parent links
+ (undo-tree-recircle tree)
+ tree))
+
+
+
(defun undo-tree-save-history (&optional filename overwrite)
"Store undo-tree history to file.
@@ -3247,12 +3375,12 @@ without asking for confirmation."
(user-error "No undo information in this buffer"))
(undo-list-transfer-to-tree)
(when (and buffer-undo-tree (not (eq buffer-undo-tree t)))
- (undo-tree-kill-visualizer)
- ;; should be cleared already by killing the visualizer, but writes
- ;; unreasable data if not for some reason, so just in case...
- (undo-tree-clear-visualizer-data buffer-undo-tree)
+ ;; (undo-tree-kill-visualizer)
+ ;; ;; should be cleared already by killing the visualizer, but writes
+ ;; ;; unreasable data if not for some reason, so just in case...
+ ;; (undo-tree-clear-visualizer-data buffer-undo-tree)
(let ((buff (current-buffer))
- tree)
+ (tree buffer-undo-tree))
;; get filename
(unless filename
(setq filename
@@ -3262,45 +3390,40 @@ without asking for confirmation."
(when (or (not (file-exists-p filename))
overwrite
(yes-or-no-p (format "Overwrite \"%s\"? " filename)))
- ;; transform undo-tree into non-circular structure, and make tmp copy
- (setq tree (undo-tree-copy buffer-undo-tree))
- (undo-tree-decircle tree)
- ;; discard undo-tree object pool before saving
- (setf (undo-tree-object-pool tree) nil)
- ;; run pre-save transformer functions
- (when undo-tree-pre-save-element-functions
- (undo-tree-mapc
- (lambda (node)
- (let ((changeset (undo-tree-node-undo node)))
- (run-hook-wrapped
- 'undo-tree-pre-save-element-functions
- (lambda (fun)
- (setq changeset (delq nil (mapcar fun changeset)))))
- (setf (undo-tree-node-undo node) changeset))
- (let ((changeset (undo-tree-node-redo node)))
- (run-hook-wrapped
- 'undo-tree-pre-save-element-functions
- (lambda (fun)
- (setq changeset (delq nil (mapcar fun changeset)))))
- (setf (undo-tree-node-redo node) changeset)))
- (undo-tree-root tree)))
+
;; print undo-tree to file
- ;; NOTE: We use `with-temp-buffer' instead of `with-temp-file' to
+ ;; Note: We use `with-temp-buffer' instead of `with-temp-file' to
;; allow `auto-compression-mode' to take effect, in case user
;; has overridden or advised the default
;; `undo-tree-make-history-save-file-name' to add a compressed
;; file extension.
- (with-auto-compression-mode
- (with-temp-buffer
- (prin1 (sha1 buff) (current-buffer))
- (terpri (current-buffer))
- (let ((print-circle t)
- (print-length nil)
- (print-level nil))
- (prin1 tree (current-buffer)))
+ (with-temp-buffer
+ ;; write version number; (original save file format (version 0) has
no version string)
+ (unless (= undo-tree-save-format-version 0)
+ (prin1 (cons 'undo-tree-save-format-version
undo-tree-save-format-version)
+ (current-buffer))
+ (terpri (current-buffer)))
+ ;; write hash
+ (prin1 (sha1 buff) (current-buffer))
+ (terpri (current-buffer))
+ ;; write tree
+ (cl-case undo-tree-save-format-version
+ (0 (undo-tree-serialize-old-format tree))
+ (1 (undo-tree-serialize tree))
+ (t (error "Unknown `undo-tree-save-format-version'; undo-tree
history *not* saved")))
+ ;; write file
+ (with-auto-compression-mode
(write-region nil nil filename)))))))
+(defmacro undo-tree--catch-load-history-error (error-fmt &rest body)
+ `(condition-case nil
+ (progn ,@body)
+ (error
+ (kill-buffer nil)
+ (funcall (if noerror #'message #'user-error) ,error-fmt filename)
+ (throw 'load-error nil))))
+
(defun undo-tree-load-history (&optional filename noerror)
"Load undo-tree history from file, for the current buffer.
@@ -3323,65 +3446,54 @@ Note this will overwrite any existing undo history."
(undo-tree-make-history-save-file-name buffer-file-name)
(expand-file-name (read-file-name "File to load from: ") nil))))
- ;; attempt to read undo-tree from FILENAME
+ ;; attempt to read undo-tree
(catch 'load-error
(unless (file-exists-p filename)
(if noerror
(throw 'load-error nil)
- (error "File \"%s\" does not exist; could not load undo-tree history"
- filename)))
- (let (buff hash tree)
- (setq buff (current-buffer))
- (with-auto-compression-mode
+ (user-error "File \"%s\" does not exist; could not load undo-tree
history"
+ filename)))
+
+ ;; read file contents
+ (let ((buff (current-buffer))
+ version hash tree)
(with-temp-buffer
- (insert-file-contents filename)
+ (with-auto-compression-mode (insert-file-contents filename))
(goto-char (point-min))
- (condition-case nil
- (setq hash (read (current-buffer)))
- (error
- (kill-buffer nil)
- (funcall (if noerror #'message #'user-error)
- "Error reading undo-tree history from \"%s\"" filename)
- (throw 'load-error nil)))
- (unless (string= (sha1 buff) hash)
- (kill-buffer nil)
- (funcall (if noerror 'message 'user-error)
- "Buffer has been modified; could not load undo-tree
history")
- (throw 'load-error nil))
- (condition-case nil
- (setq tree (read (current-buffer)))
- (error
- (kill-buffer nil)
- (funcall (if noerror #'message #'error)
- "Error reading undo-tree history from \"%s\"" filename)
- (throw 'load-error nil)))
- (kill-buffer nil)))
- ;; run post-load transformer functions
- (when undo-tree-post-load-element-functions
- (undo-tree-mapc
- (lambda (node)
- (let ((changeset (undo-tree-node-undo node)))
- (run-hook-wrapped
- 'undo-tree-post-load-element-functions
- (lambda (fun)
- (setq changeset (delq nil (mapcar fun changeset)))))
- (setf (undo-tree-node-undo node) changeset))
- (let ((changeset (undo-tree-node-redo node)))
- (run-hook-wrapped
- 'undo-tree-post-load-element-functions
- (lambda (fun)
- (setq changeset (delq nil (mapcar fun changeset)))))
- (setf (undo-tree-node-redo node) changeset)))
- (undo-tree-root tree))) ;; initialise empty undo-tree object
pool
- (setf (undo-tree-object-pool tree)
- (make-hash-table :test 'eq :weakness 'value))
- ;; restore circular undo-tree data structure
- (undo-tree-recircle tree)
- ;; create undo-tree object pool
- (setf (undo-tree-object-pool tree)
- (make-hash-table :test 'eq :weakness 'value))
- (setq buffer-undo-tree tree
- buffer-undo-list '(nil undo-tree-canary)))))
+
+ (undo-tree--catch-load-history-error
+ "Error reading undo-tree history from \"%s\""
+ ;; read version number
+ (setq version (read (current-buffer)))
+ ;; read hash
+ (cond
+ ((eq (car-safe version) 'undo-tree-save-format-version)
+ (setq version (cdr version))
+ (setq hash (read (current-buffer))))
+ ;; original save file format (version 0) has no version string
+ ((stringp version)
+ (setq hash version
+ version 0))
+ (t (error "Error"))))
+
+ ;; check hash
+ (undo-tree--catch-load-history-error
+ "Buffer has been modified since undo-tree history was saved to
+ \"%s\"; could not load undo-tree history"
+ (unless (string= (sha1 buff) hash) (error "Error")))
+
+ ;; read tree
+ (undo-tree--catch-load-history-error
+ "Error reading undo-tree history from \"%s\""
+ (setq tree
+ (cl-case version
+ (0 (undo-tree-deserialize-old-format))
+ (1 (undo-tree-deserialize))
+ (t (error "Error")))))
+ (kill-buffer nil))
+
+ (setq buffer-undo-tree tree
+ buffer-undo-list (list nil 'undo-tree-canary)))))
@@ -3449,11 +3561,8 @@ Note this will overwrite any existing undo history."
(setq undo-tree-visualizer-initial-node (undo-tree-current undo-tree))
(setq undo-tree-visualizer-spacing
(undo-tree-visualizer-calculate-spacing))
- (make-local-variable 'undo-tree-visualizer-timestamps)
- (make-local-variable 'undo-tree-visualizer-diff)
(setq buffer-undo-tree undo-tree)
(undo-tree-visualizer-mode)
- ;; FIXME; don't know why `undo-tree-visualizer-mode' clears this
(setq buffer-undo-tree undo-tree)
(set (make-local-variable 'undo-tree-visualizer-lazy-drawing)
(or (eq undo-tree-visualizer-lazy-drawing t)
@@ -3737,9 +3846,9 @@ Note this will overwrite any existing undo history."
undo-tree-insert-face
(nconc
(cond
- (current '(undo-tree-visualizer-current-face))
- (unmodified '(undo-tree-visualizer-unmodified-face))
- (register '(undo-tree-visualizer-register-face)))
+ (current (list 'undo-tree-visualizer-current-face))
+ (unmodified (list 'undo-tree-visualizer-unmodified-face))
+ (register (list 'undo-tree-visualizer-register-face)))
undo-tree-insert-face))
;; draw node and link it to its representation in visualizer
(undo-tree-insert node-string)
@@ -4086,7 +4195,9 @@ Within the undo-tree visualizer, the following keys are
available:
:abbrev-table nil
(setq truncate-lines t)
(setq cursor-type nil)
- (setq undo-tree-visualizer-selected-node nil))
+ (setq undo-tree-visualizer-selected-node nil)
+ (make-local-variable 'undo-tree-visualizer-timestamps)
+ (make-local-variable 'undo-tree-visualizer-diff))
(define-minor-mode undo-tree-visualizer-selection-mode
- [elpa] externals/undo-tree updated (bf2e9ba0c9 -> 1f8d005409), Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 5da2a7aee9 1/9: Implement new undo-tree save format., Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 35a438bee8 3/9: Fix how undo-tree-viualizer-timestamps|diff are set buffer-local., Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 594ed6a788 5/9: Fix URLs in package headers., Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 2bf5e230f1 7/9: Update contact email address in package headers., Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree cbe0c708d8 4/9: Fix potential insidious list creation bugs., Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 1f8d005409 9/9: * undo-tree.el: Various cosmetic changes, Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 25709c426d 2/9: Add missing Package-Requires header for queue dependency., Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 42aab056e3 6/9: Fix bug in detection of nodes corresponding to unmodified buffers., Stefan Monnier, 2022/03/12
- [elpa] externals/undo-tree 7171865096 8/9: Merge remote-tracking branch 'upstream/undo-tree/main' into externals/undo-tree,
Stefan Monnier <=