(eval-when-compile (require 'cl))
(require 'diff)
(unless (fboundp 'characterp)
(defalias 'characterp 'char-valid-p))
(unless (fboundp 'region-active-p)
(defun region-active-p () (and transient-mark-mode mark-active)))
(unless (fboundp 'registerv-make)
(defmacro registerv-make (data &rest _dummy) data))
(unless (fboundp 'registerv-data)
(defmacro registerv-data (data) data))
(unless (fboundp 'diff-no-select)
(defun diff-no-select (old new &optional switches no-async buf)
(unless (bufferp new) (setq new (expand-file-name new)))
(unless (bufferp old) (setq old (expand-file-name old)))
(or switches (setq switches diff-switches)) (unless (listp switches) (setq switches (list switches)))
(or buf (setq buf (get-buffer-create "*Diff*")))
(let* ((old-alt (diff-file-local-copy old))
(new-alt (diff-file-local-copy new))
(command
(mapconcat 'identity
`(,diff-command
,@switches
,@(mapcar #'shell-quote-argument
(nconc
(when (or old-alt new-alt)
(list "-L" (if (stringp old)
old (prin1-to-string old))
"-L" (if (stringp new)
new (prin1-to-string new))))
(list (or old-alt old)
(or new-alt new)))))
" "))
(thisdir default-directory))
(with-current-buffer buf
(setq buffer-read-only t)
(buffer-disable-undo (current-buffer))
(let ((inhibit-read-only t))
(erase-buffer))
(buffer-enable-undo (current-buffer))
(diff-mode)
(set (make-local-variable 'revert-buffer-function)
(lambda (_ignore-auto _noconfirm)
(diff-no-select old new switches no-async (current-buffer))))
(setq default-directory thisdir)
(let ((inhibit-read-only t))
(insert command "\n"))
(if (and (not no-async) (fboundp 'start-process))
(let ((proc (start-process "Diff" buf shell-file-name
shell-command-switch command)))
(set-process-filter proc 'diff-process-filter)
(set-process-sentinel
proc (lambda (proc _msg)
(with-current-buffer (process-buffer proc)
(diff-sentinel (process-exit-status proc))
(if old-alt (delete-file old-alt))
(if new-alt (delete-file new-alt))))))
(let ((inhibit-read-only t))
(diff-sentinel
(call-process shell-file-name nil buf nil
shell-command-switch command))
(if old-alt (delete-file old-alt))
(if new-alt (delete-file new-alt)))))
buf)))
(unless (fboundp 'diff-file-local-copy)
(defun diff-file-local-copy (file-or-buf)
(if (bufferp file-or-buf)
(with-current-buffer file-or-buf
(let ((tempfile (make-temp-file "buffer-content-")))
(write-region nil nil tempfile nil 'nomessage)
tempfile))
(file-local-copy file-or-buf))))
(unless (fboundp 'user-error)
(defalias 'user-error 'error)
(add-to-list 'debug-ignored-errors "^No further undo information")
(add-to-list 'debug-ignored-errors "^No further redo information")
(add-to-list 'debug-ignored-errors "^No further redo information for region"))
(defvar buffer-undo-tree nil
"Tree of undo entries in current buffer.")
(put 'buffer-undo-tree 'permanent-local t)
(make-variable-buffer-local 'buffer-undo-tree)
(defgroup undo-tree nil
"Tree undo/redo."
:group 'undo)
(defcustom undo-tree-mode-lighter " Undo-Tree"
"Lighter displayed in mode line
when `undo-tree-mode' is enabled."
:group 'undo-tree
:type 'string)
(defcustom undo-tree-incompatible-major-modes '(term-mode)
"List of major-modes in which `undo-tree-mode' should not be enabled.
\(See `turn-on-undo-tree-mode'.\)"
:group 'undo-tree
:type '(repeat symbol))
(defcustom undo-tree-enable-undo-in-region t
"When non-nil, enable undo-in-region.
When undo-in-region is enabled, undoing or redoing when the
region is active (in `transient-mark-mode') or with a prefix
argument (not in `transient-mark-mode') only undoes changes
within the current region."
:group 'undo-tree
:type 'boolean)
(defcustom undo-tree-auto-save-history nil
"When non-nil, `undo-tree-mode' will save undo history to file
when a buffer is saved to file.
It will automatically load undo history when a buffer is loaded
from file, if an undo save file exists.
By default, undo-tree history is saved to a file called
\".<buffer-file-name>.~undo-tree~\" in the same directory as the
file itself. To save under a different directory, customize
`undo-tree-history-directory-alist' (see the documentation for
that variable for details).
WARNING! `undo-tree-auto-save-history' will not work properly in
Emacs versions prior to 24.3, so it cannot be enabled via
the customization interface in versions earlier than that one. To
ignore this warning and enable it regardless, set
`undo-tree-auto-save-history' to a non-nil value outside of
customize."
:group 'undo-tree
:type (if (version-list-< (version-to-list emacs-version) '(24 3))
'(choice (const :tag "<disabled>" nil))
'boolean))
(defcustom undo-tree-history-directory-alist nil
"Alist of filename patterns and undo history directory names.
Each element looks like (REGEXP . DIRECTORY). Undo history for
files with names matching REGEXP will be saved in DIRECTORY.
DIRECTORY may be relative or absolute. If it is absolute, so
that all matching files are backed up into the same directory,
the file names in this directory will be the full name of the
file backed up with all directory separators changed to `!' to
prevent clashes. This will not work correctly if your filesystem
truncates the resulting name.
For the common case of all backups going into one directory, the
alist should contain a single element pairing \".\" with the
appropriate directory name.
If this variable is nil, or it fails to match a filename, the
backup is made in the original file's directory.
On MS-DOS filesystems without long names this variable is always
ignored."
:group 'undo-tree
:type '(repeat (cons (regexp :tag "Regexp matching filename")
(directory :tag "Undo history directory name"))))
(defcustom undo-tree-visualizer-relative-timestamps t
"When non-nil, display times relative to current time
when displaying time stamps in visualizer.
Otherwise, display absolute times."
:group 'undo-tree
:type 'boolean)
(defcustom undo-tree-visualizer-timestamps nil
"When non-nil, display time-stamps by default
in undo-tree visualizer.
\\<undo-tree-visualizer-mode-map>You can always toggle time-stamps on and off \
using \\[undo-tree-visualizer-toggle-timestamps], regardless of the
setting of this variable."
:group 'undo-tree
:type 'boolean)
(defcustom undo-tree-visualizer-diff nil
"When non-nil, display diff by default in undo-tree visualizer.
\\<undo-tree-visualizer-mode-map>You can always toggle the diff display \
using \\[undo-tree-visualizer-toggle-diff], regardless of the
setting of this variable."
:group 'undo-tree
:type 'boolean)
(defcustom undo-tree-visualizer-lazy-drawing 100
"When non-nil, use lazy undo-tree drawing in visualizer.
Setting this to a number causes the visualizer to switch to lazy
drawing when the number of nodes in the tree is larger than this
value.
Lazy drawing means that only the visible portion of the tree will
be drawn initially, and the tree will be extended later as
needed. For the most part, the only visible effect of this is to
significantly speed up displaying the visualizer for very large
trees.
There is one potential negative effect of lazy drawing. Other
branches of the tree will only be drawn once the node from which
they branch off becomes visible. So it can happen that certain
portions of the tree that would be shown with lazy drawing
disabled, will not be drawn immediately when it is
enabled. However, this effect is quite rare in practice."
:group 'undo-tree
:type '(choice (const :tag "never" nil)
(const :tag "always" t)
(integer :tag "> size")))
(defface undo-tree-visualizer-default-face
'((((class color)) :foreground "gray"))
"Face used to draw undo-tree in visualizer."
:group 'undo-tree)
(defface undo-tree-visualizer-current-face
'((((class color)) :foreground "red"))
"Face used to highlight current undo-tree node in visualizer."
:group 'undo-tree)
(defface undo-tree-visualizer-active-branch-face
'((((class color) (background dark))
(:foreground "white" :weight bold))
(((class color) (background light))
(:foreground "black" :weight bold)))
"Face used to highlight active undo-tree branch in visualizer."
:group 'undo-tree)
(defface undo-tree-visualizer-register-face
'((((class color)) :foreground "yellow"))
"Face used to highlight undo-tree nodes saved to a register
in visualizer."
:group 'undo-tree)
(defface undo-tree-visualizer-unmodified-face
'((((class color)) :foreground "cyan"))
"Face used to highlight nodes corresponding to unmodified buffers
in visualizer."
:group 'undo-tree)
(defvar undo-tree-visualizer-parent-buffer nil
"Parent buffer in visualizer.")
(put 'undo-tree-visualizer-parent-buffer 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-parent-buffer)
(defvar undo-tree-visualizer-parent-mtime nil)
(put 'undo-tree-visualizer-parent-mtime 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-parent-mtime)
(defvar undo-tree-visualizer-spacing nil)
(put 'undo-tree-visualizer-spacing 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-spacing)
(defsubst undo-tree-visualizer-calculate-spacing ()
(if undo-tree-visualizer-timestamps
(if undo-tree-visualizer-relative-timestamps 9 13)
3))
(defvar undo-tree-visualizer-initial-node nil)
(put 'undo-tree-visualizer-initial-node 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-initial-node)
(defvar undo-tree-visualizer-selected-node nil)
(put 'undo-tree-visualizer-selected-node 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-selected)
(defvar undo-tree-visualizer-needs-extending-down nil)
(put 'undo-tree-visualizer-needs-extending-down 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-needs-extending-down)
(defvar undo-tree-visualizer-needs-extending-up nil)
(put 'undo-tree-visualizer-needs-extending-up 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-needs-extending-up)
(defvar undo-tree-inhibit-kill-visualizer nil)
(defvar undo-tree-insert-face nil)
(defconst undo-tree-visualizer-buffer-name " *undo-tree*")
(defconst undo-tree-diff-buffer-name "*undo-tree Diff*")
(add-hook 'write-file-functions 'undo-tree-save-history-hook)
(add-hook 'find-file-hook 'undo-tree-load-history-hook)
(defvar undo-tree-map nil
"Keymap used in undo-tree-mode.")
(unless undo-tree-map
(let ((map (make-sparse-keymap)))
(define-key map [remap undo] 'undo-tree-undo)
(define-key map [remap undo-only] 'undo-tree-undo)
(define-key map (kbd "C-/") 'undo-tree-undo)
(define-key map "\C-_" 'undo-tree-undo)
(define-key map (kbd "C-?") 'undo-tree-redo)
(define-key map (kbd "M-_") 'undo-tree-redo)
(define-key map [remap redo] 'undo-tree-redo)
(define-key map (kbd "\C-x u") 'undo-tree-visualize)
(define-key map (kbd "C-x r u") 'undo-tree-save-state-to-register)
(define-key map (kbd "C-x r U") 'undo-tree-restore-state-from-register)
(setq undo-tree-map map)))
(defvar undo-tree-visualizer-mode-map nil
"Keymap used in undo-tree visualizer.")
(unless undo-tree-visualizer-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [remap previous-line] 'undo-tree-visualize-undo)
(define-key map [remap next-line] 'undo-tree-visualize-redo)
(define-key map [up] 'undo-tree-visualize-undo)
(define-key map "p" 'undo-tree-visualize-undo)
(define-key map "\C-p" 'undo-tree-visualize-undo)
(define-key map [down] 'undo-tree-visualize-redo)
(define-key map "n" 'undo-tree-visualize-redo)
(define-key map "\C-n" 'undo-tree-visualize-redo)
(define-key map [remap forward-char]
'undo-tree-visualize-switch-branch-right)
(define-key map [remap backward-char]
'undo-tree-visualize-switch-branch-left)
(define-key map [right] 'undo-tree-visualize-switch-branch-right)
(define-key map "f" 'undo-tree-visualize-switch-branch-right)
(define-key map "\C-f" 'undo-tree-visualize-switch-branch-right)
(define-key map [left] 'undo-tree-visualize-switch-branch-left)
(define-key map "b" 'undo-tree-visualize-switch-branch-left)
(define-key map "\C-b" 'undo-tree-visualize-switch-branch-left)
(define-key map [remap backward-paragraph] 'undo-tree-visualize-undo-to-x)
(define-key map [remap forward-paragraph] 'undo-tree-visualize-redo-to-x)
(define-key map "\M-{" 'undo-tree-visualize-undo-to-x)
(define-key map "\M-}" 'undo-tree-visualize-redo-to-x)
(define-key map [C-up] 'undo-tree-visualize-undo-to-x)
(define-key map [C-down] 'undo-tree-visualize-redo-to-x)
(define-key map [mouse-1] 'undo-tree-visualizer-mouse-set)
(define-key map "t" 'undo-tree-visualizer-toggle-timestamps)
(define-key map "d" 'undo-tree-visualizer-toggle-diff)
(define-key map "s" 'undo-tree-visualizer-selection-mode)
(define-key map "," 'undo-tree-visualizer-scroll-left)
(define-key map "." 'undo-tree-visualizer-scroll-right)
(define-key map "<" 'undo-tree-visualizer-scroll-left)
(define-key map ">" 'undo-tree-visualizer-scroll-right)
(define-key map [next] 'undo-tree-visualizer-scroll-up)
(define-key map [prior] 'undo-tree-visualizer-scroll-down)
(define-key map "q" 'undo-tree-visualizer-quit)
(define-key map "\C-q" 'undo-tree-visualizer-abort)
(setq undo-tree-visualizer-mode-map map)))
(defvar undo-tree-visualizer-selection-mode-map nil
"Keymap used in undo-tree visualizer selection mode.")
(unless undo-tree-visualizer-selection-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [remap previous-line]
'undo-tree-visualizer-select-previous)
(define-key map [remap next-line]
'undo-tree-visualizer-select-next)
(define-key map [up] 'undo-tree-visualizer-select-previous)
(define-key map "p" 'undo-tree-visualizer-select-previous)
(define-key map "\C-p" 'undo-tree-visualizer-select-previous)
(define-key map [down] 'undo-tree-visualizer-select-next)
(define-key map "n" 'undo-tree-visualizer-select-next)
(define-key map "\C-n" 'undo-tree-visualizer-select-next)
(define-key map [next]
(lambda () (interactive) (undo-tree-visualizer-select-next 10)))
(define-key map [prior]
(lambda () (interactive) (undo-tree-visualizer-select-previous 10)))
(define-key map [remap forward-char] 'undo-tree-visualizer-select-right)
(define-key map [remap backward-char] 'undo-tree-visualizer-select-left)
(define-key map [right] 'undo-tree-visualizer-select-right)
(define-key map "f" 'undo-tree-visualizer-select-right)
(define-key map "\C-f" 'undo-tree-visualizer-select-right)
(define-key map [left] 'undo-tree-visualizer-select-left)
(define-key map "b" 'undo-tree-visualizer-select-left)
(define-key map "\C-b" 'undo-tree-visualizer-select-left)
(define-key map ","
(lambda () (interactive) (undo-tree-visualizer-select-left 10)))
(define-key map "."
(lambda () (interactive) (undo-tree-visualizer-select-right 10)))
(define-key map "<"
(lambda () (interactive) (undo-tree-visualizer-select-left 10)))
(define-key map ">"
(lambda () (interactive) (undo-tree-visualizer-select-right 10)))
(define-key map "\r" 'undo-tree-visualizer-set)
(define-key map [mouse-1] 'undo-tree-visualizer-mouse-select)
(define-key map "d" 'undo-tree-visualizer-selection-toggle-diff)
(setq undo-tree-visualizer-selection-mode-map map)))
(defvar undo-tree-old-undo-menu-item nil)
(defun undo-tree-update-menu-bar ()
"Update `undo-tree-mode' Edit menu items."
(if undo-tree-mode
(progn
(setq undo-tree-old-undo-menu-item
(cdr (assq 'undo (lookup-key global-map [menu-bar edit]))))
(define-key (lookup-key global-map [menu-bar edit])
[undo] '(menu-item "Undo" undo-tree-undo
:enable (and undo-tree-mode
(not buffer-read-only)
(not (eq t buffer-undo-list))
(not (eq nil buffer-undo-tree))
(undo-tree-node-previous
(undo-tree-current buffer-undo-tree)))
:help "Undo last operation"))
(define-key-after (lookup-key global-map [menu-bar edit])
[redo] '(menu-item "Redo" undo-tree-redo
:enable (and undo-tree-mode
(not buffer-read-only)
(not (eq t buffer-undo-list))
(not (eq nil buffer-undo-tree))
(undo-tree-node-next
(undo-tree-current buffer-undo-tree)))
:help "Redo last operation")
'undo))
(define-key (lookup-key global-map [menu-bar edit])
[undo] undo-tree-old-undo-menu-item)
(define-key (lookup-key global-map [menu-bar edit])
[redo] nil)))
(add-hook 'menu-bar-update-hook 'undo-tree-update-menu-bar)
(defstruct
(undo-tree
:named
(:constructor nil)
(:constructor make-undo-tree
(&aux
(root (undo-tree-make-node nil nil))
(current root)
(size 0)
(count 0)
(object-pool (make-hash-table :test 'eq :weakness 'value))))
)
root current size count object-pool)
(defstruct
(undo-tree-node
(:type vector) (:constructor nil)
(:constructor undo-tree-make-node
(previous undo
&optional redo
&aux
(timestamp (current-time))
(branch 0)))
(:constructor undo-tree-make-node-backwards
(next-node undo
&optional redo
&aux
(next (list next-node))
(timestamp (current-time))
(branch 0)))
(:copier nil))
previous next undo redo timestamp branch meta-data)
(defmacro undo-tree-node-p (n)
(let ((len (length (undo-tree-make-node nil nil))))
`(and (vectorp ,n) (= (length ,n) ,len))))
(defstruct
(undo-tree-region-data
(:type vector) (:constructor nil)
(:constructor undo-tree-make-region-data
(&optional undo-beginning undo-end
redo-beginning redo-end))
(:constructor undo-tree-make-undo-region-data
(undo-beginning undo-end))
(:constructor undo-tree-make-redo-region-data
(redo-beginning redo-end))
(:copier nil))
undo-beginning undo-end redo-beginning redo-end)
(defmacro undo-tree-region-data-p (r)
(let ((len (length (undo-tree-make-region-data))))
`(and (vectorp ,r) (= (length ,r) ,len))))
(defmacro undo-tree-node-clear-region-data (node)
`(setf (undo-tree-node-meta-data ,node)
(delq nil
(delq :region
(plist-put (undo-tree-node-meta-data ,node)
:region nil)))))
(defmacro undo-tree-node-undo-beginning (node)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(when (undo-tree-region-data-p r)
(undo-tree-region-data-undo-beginning r))))
(defmacro undo-tree-node-undo-end (node)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(when (undo-tree-region-data-p r)
(undo-tree-region-data-undo-end r))))
(defmacro undo-tree-node-redo-beginning (node)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(when (undo-tree-region-data-p r)
(undo-tree-region-data-redo-beginning r))))
(defmacro undo-tree-node-redo-end (node)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(when (undo-tree-region-data-p r)
(undo-tree-region-data-redo-end r))))
(defsetf undo-tree-node-undo-beginning (node) (val)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(unless (undo-tree-region-data-p r)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :region
(setq r (undo-tree-make-region-data)))))
(setf (undo-tree-region-data-undo-beginning r) ,val)))
(defsetf undo-tree-node-undo-end (node) (val)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(unless (undo-tree-region-data-p r)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :region
(setq r (undo-tree-make-region-data)))))
(setf (undo-tree-region-data-undo-end r) ,val)))
(defsetf undo-tree-node-redo-beginning (node) (val)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(unless (undo-tree-region-data-p r)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :region
(setq r (undo-tree-make-region-data)))))
(setf (undo-tree-region-data-redo-beginning r) ,val)))
(defsetf undo-tree-node-redo-end (node) (val)
`(let ((r (plist-get (undo-tree-node-meta-data ,node) :region)))
(unless (undo-tree-region-data-p r)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :region
(setq r (undo-tree-make-region-data)))))
(setf (undo-tree-region-data-redo-end r) ,val)))
(defstruct
(undo-tree-visualizer-data
(:type vector) (:constructor nil)
(:constructor undo-tree-make-visualizer-data
(&optional lwidth cwidth rwidth marker))
(:copier nil))
lwidth cwidth rwidth marker)
(defmacro undo-tree-visualizer-data-p (v)
(let ((len (length (undo-tree-make-visualizer-data))))
`(and (vectorp ,v) (= (length ,v) ,len))))
(defun undo-tree-node-clear-visualizer-data (node)
(let ((plist (undo-tree-node-meta-data node)))
(if (eq (car plist) :visualizer)
(setf (undo-tree-node-meta-data node) (nthcdr 2 plist))
(while (and plist (not (eq (cadr plist) :visualizer)))
(setq plist (cdr plist)))
(if plist (setcdr plist (nthcdr 3 plist))))))
(defmacro undo-tree-node-lwidth (node)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(when (undo-tree-visualizer-data-p v)
(undo-tree-visualizer-data-lwidth v))))
(defmacro undo-tree-node-cwidth (node)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(when (undo-tree-visualizer-data-p v)
(undo-tree-visualizer-data-cwidth v))))
(defmacro undo-tree-node-rwidth (node)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(when (undo-tree-visualizer-data-p v)
(undo-tree-visualizer-data-rwidth v))))
(defmacro undo-tree-node-marker (node)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(when (undo-tree-visualizer-data-p v)
(undo-tree-visualizer-data-marker v))))
(defsetf undo-tree-node-lwidth (node) (val)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(unless (undo-tree-visualizer-data-p v)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :visualizer
(setq v (undo-tree-make-visualizer-data)))))
(setf (undo-tree-visualizer-data-lwidth v) ,val)))
(defsetf undo-tree-node-cwidth (node) (val)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(unless (undo-tree-visualizer-data-p v)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :visualizer
(setq v (undo-tree-make-visualizer-data)))))
(setf (undo-tree-visualizer-data-cwidth v) ,val)))
(defsetf undo-tree-node-rwidth (node) (val)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(unless (undo-tree-visualizer-data-p v)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :visualizer
(setq v (undo-tree-make-visualizer-data)))))
(setf (undo-tree-visualizer-data-rwidth v) ,val)))
(defsetf undo-tree-node-marker (node) (val)
`(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer)))
(unless (undo-tree-visualizer-data-p v)
(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :visualizer
(setq v (undo-tree-make-visualizer-data)))))
(setf (undo-tree-visualizer-data-marker v) ,val)))
(defstruct
(undo-tree-register-data
(:type vector)
(:constructor nil)
(:constructor undo-tree-make-register-data (buffer node)))
buffer node)
(defun undo-tree-register-data-p (data)
(and (vectorp data)
(= (length data) 2)
(undo-tree-node-p (undo-tree-register-data-node data))))
(defun undo-tree-register-data-print-func (data)
(princ (format "an undo-tree state for buffer %s"
(undo-tree-register-data-buffer data))))
(defmacro undo-tree-node-register (node)
`(plist-get (undo-tree-node-meta-data ,node) :register))
(defsetf undo-tree-node-register (node) (val)
`(setf (undo-tree-node-meta-data ,node)
(plist-put (undo-tree-node-meta-data ,node) :register ,val)))
(defun undo-tree-grow (undo)
"Add an UNDO node to current branch of `buffer-undo-tree'."
(let* ((current (undo-tree-current buffer-undo-tree))
(new (undo-tree-make-node current undo)))
(push new (undo-tree-node-next current))
(setf (undo-tree-current buffer-undo-tree) new)))
(defun undo-tree-grow-backwards (node undo &optional redo)
"Add new node *above* undo-tree NODE, and return new node.
Note that this will overwrite NODE's \"previous\" link, so should
only be used on a detached NODE, never on nodes that are already
part of `buffer-undo-tree'."
(let ((new (undo-tree-make-node-backwards node undo redo)))
(setf (undo-tree-node-previous node) new)
new))
(defun undo-tree-splice-node (node splice)
"Splice NODE into undo tree, below node SPLICE.
Note that this will overwrite NODE's \"next\" and \"previous\"
links, so should only be used on a detached NODE, never on nodes
that are already part of `buffer-undo-tree'."
(setf (undo-tree-node-next node) (undo-tree-node-next splice)
(undo-tree-node-branch node) (undo-tree-node-branch splice)
(undo-tree-node-previous node) splice
(undo-tree-node-next splice) (list node)
(undo-tree-node-branch splice) 0)
(dolist (n (undo-tree-node-next node))
(setf (undo-tree-node-previous n) node)))
(defun undo-tree-snip-node (node)
"Snip NODE out of undo tree."
(let* ((parent (undo-tree-node-previous node))
position p)
(if (= (length (undo-tree-node-next parent)) 0)
(setf (undo-tree-node-next parent) (undo-tree-node-next node)
(undo-tree-node-branch parent) (undo-tree-node-branch node))
(setq position (undo-tree-position node (undo-tree-node-next parent)))
(cond
((= (undo-tree-node-branch parent) position)
(setf (undo-tree-node-branch parent)
(+ position (undo-tree-node-branch node))))
((> (undo-tree-node-branch parent) position)
(incf (undo-tree-node-branch parent)
(1- (length (undo-tree-node-next node))))))
(if (= position 0)
(setf (undo-tree-node-next parent)
(nconc (undo-tree-node-next node)
(cdr (undo-tree-node-next parent))))
(setq p (nthcdr (1- position) (undo-tree-node-next parent)))
(setcdr p (nconc (undo-tree-node-next node) (cddr p)))))
(dolist (n (undo-tree-node-next node))
(setf (undo-tree-node-previous n) parent))))
(defun undo-tree-mapc (--undo-tree-mapc-function-- node)
(let ((stack (list node))
n)
(while stack
(setq n (pop stack))
(funcall --undo-tree-mapc-function-- n)
(setq stack (append (undo-tree-node-next n) stack)))))
(defmacro undo-tree-num-branches ()
"Return number of branches at current undo tree node."
'(length (undo-tree-node-next (undo-tree-current buffer-undo-tree))))
(defun undo-tree-position (node list)
"Find the first occurrence of NODE in LIST.
Return the index of the matching item, or nil of not found.
Comparison is done with `eq'."
(let ((i 0))
(catch 'found
(while (progn
(when (eq node (car list)) (throw 'found i))
(incf i)
(setq list (cdr list))))
nil)))
(defvar *undo-tree-id-counter* 0)
(make-variable-buffer-local '*undo-tree-id-counter*)
(defmacro undo-tree-generate-id ()
`(let ((num (prog1 *undo-tree-id-counter* (incf *undo-tree-id-counter*))))
(make-symbol (format "undo-tree-id%d" num))))
(defun undo-tree-decircle (undo-tree)
(undo-tree-mapc
(lambda (node)
(dolist (n (undo-tree-node-next node))
(setf (undo-tree-node-previous n) nil)))
(undo-tree-root undo-tree)))
(defun undo-tree-recircle (undo-tree)
(undo-tree-mapc
(lambda (node)
(dolist (n (undo-tree-node-next node))
(setf (undo-tree-node-previous n) node)))
(undo-tree-root undo-tree)))
(defmacro undo-list-marker-elt-p (elt)
`(markerp (car-safe ,elt)))
(defmacro undo-list-GCd-marker-elt-p (elt)
`(and (car-safe ,elt)
(symbolp (car ,elt))
(let ((str (symbol-name (car ,elt))))
(and (> (length str) 12)
(string= (substring str 0 12) "undo-tree-id")))
(numberp (cdr-safe ,elt))))
(defun undo-tree-move-GC-elts-to-pool (elt)
(when (undo-list-marker-elt-p elt)
(let ((id (undo-tree-generate-id)))
(puthash id (car elt) (undo-tree-object-pool buffer-undo-tree))
(setcar elt id))))
(defun undo-tree-restore-GC-elts-from-pool (elt)
(if (undo-list-GCd-marker-elt-p elt)
(when (setcar elt (gethash (car elt)
(undo-tree-object-pool buffer-undo-tree)))
elt)
elt))
(defun undo-list-clean-GCd-elts (undo-list)
(while (undo-list-GCd-marker-elt-p (car undo-list))
(unless (gethash (caar undo-list)
(undo-tree-object-pool buffer-undo-tree))
(setq undo-list (cdr undo-list))))
(let ((p undo-list))
(while (cdr p)
(when (and (undo-list-GCd-marker-elt-p (cadr p))
(null (gethash (car (cadr p))
(undo-tree-object-pool buffer-undo-tree))))
(setcdr p (cddr p)))
(setq p (cdr p))))
undo-list)
(defun undo-list-pop-changeset (&optional discard-pos)
(while (or (null (car buffer-undo-list))
(and discard-pos (integerp (car buffer-undo-list))))
(setq buffer-undo-list (cdr buffer-undo-list)))
(if (eq (car buffer-undo-list) 'undo-tree-canary)
(push nil buffer-undo-list)
(let* ((changeset (list (pop buffer-undo-list)))
(p changeset))
(while (progn
(undo-tree-move-GC-elts-to-pool (car p))
(while (and discard-pos (integerp (car buffer-undo-list)))
(setq buffer-undo-list (cdr buffer-undo-list)))
(and (car buffer-undo-list)
(not (eq (car buffer-undo-list) 'undo-tree-canary))))
(setcdr p (list (pop buffer-undo-list)))
(setq p (cdr p)))
changeset)))
(defun undo-tree-copy-list (undo-list)
(let (copy p)
(while (and undo-list (null copy))
(setq copy
(undo-tree-restore-GC-elts-from-pool (pop undo-list))))
(when copy
(setq copy (list copy)
p copy)
(while undo-list
(when (setcdr p (undo-tree-restore-GC-elts-from-pool
(undo-copy-list-1 (pop undo-list))))
(setcdr p (list (cdr p)))
(setq p (cdr p))))
copy)))
(defun undo-list-transfer-to-tree ()
(assert (not (eq buffer-undo-tree t)))
(when (null buffer-undo-tree) (setq buffer-undo-tree (make-undo-tree)))
(when (null buffer-undo-list)
(setq buffer-undo-list '(nil undo-tree-canary)))
(unless (or (eq (cadr buffer-undo-list) 'undo-tree-canary)
(eq (car buffer-undo-list) 'undo-tree-canary))
(let* ((node (undo-tree-make-node nil (undo-list-pop-changeset)))
(splice (undo-tree-current buffer-undo-tree))
(size (undo-list-byte-size (undo-tree-node-undo node)))
(count 1))
(setf (undo-tree-current buffer-undo-tree) node)
(while (and buffer-undo-list
(not (eq (cadr buffer-undo-list) 'undo-tree-canary)))
(setq node
(undo-tree-grow-backwards node (undo-list-pop-changeset)))
(incf size (undo-list-byte-size (undo-tree-node-undo node)))
(incf count))
(if (or (eq (cadr buffer-undo-list) 'undo-tree-canary)
(eq (car buffer-undo-list) 'undo-tree-canary))
(progn
(setf (undo-tree-node-previous node) splice)
(push node (undo-tree-node-next splice))
(setf (undo-tree-node-branch splice) 0)
(incf (undo-tree-size buffer-undo-tree) size)
(incf (undo-tree-count buffer-undo-tree) count))
(setq node (undo-tree-grow-backwards node nil))
(setf (undo-tree-root buffer-undo-tree) node)
(setq buffer-undo-list '(nil undo-tree-canary))
(setf (undo-tree-size buffer-undo-tree) size)
(setf (undo-tree-count buffer-undo-tree) count)
(setq buffer-undo-list '(nil undo-tree-canary))))
(undo-tree-discard-history)))
(defun undo-list-byte-size (undo-list)
(let ((size 0) (p undo-list))
(while p
(incf size 8) (when (and (consp (car p)) (stringp (caar p)))
(incf size (string-bytes (caar p))))
(setq p (cdr p)))
size))
(defun undo-list-rebuild-from-tree ()
"Rebuild `buffer-undo-list' from information in `buffer-undo-tree'."
(unless (eq buffer-undo-list t)
(undo-list-transfer-to-tree)
(setq buffer-undo-list nil)
(when buffer-undo-tree
(let ((stack (list (list (undo-tree-root buffer-undo-tree)))))
(push (sort (mapcar 'identity (undo-tree-node-next (caar stack)))
(lambda (a b)
(time-less-p (undo-tree-node-timestamp a)
(undo-tree-node-timestamp b))))
stack)
(while (or (car stack)
(not (eq (car (nth 1 stack))
(undo-tree-current buffer-undo-tree))))
(if (car stack)
(progn
(setq buffer-undo-list
(append (undo-tree-node-undo (caar stack))
buffer-undo-list))
(undo-boundary)
(push (sort (mapcar 'identity
(undo-tree-node-next (caar stack)))
(lambda (a b)
(time-less-p (undo-tree-node-timestamp a)
(undo-tree-node-timestamp b))))
stack))
(pop stack)
(setq buffer-undo-list
(append (undo-tree-node-redo (caar stack))
buffer-undo-list))
(undo-boundary)
(pop (car stack))))))))
(defun undo-tree-oldest-leaf (node)
(while (undo-tree-node-next node)
(setq node
(car (sort (mapcar 'identity (undo-tree-node-next node))
(lambda (a b)
(time-less-p (undo-tree-node-timestamp a)
(undo-tree-node-timestamp b)))))))
node)
(defun undo-tree-discard-node (node)
(unless (eq node (undo-tree-current buffer-undo-tree))
(if (eq node (undo-tree-root buffer-undo-tree))
(cond
((> (length (undo-tree-node-next node)) 1)
(error "Trying to discard undo-tree root which still\
has multiple branches"))
((eq (car (undo-tree-node-next node))
(undo-tree-current buffer-undo-tree))
nil)
(t
(let ((r (undo-tree-node-register node)))
(when (and r (eq (get-register r) node))
(set-register r nil)))
(setq node (setf (undo-tree-root buffer-undo-tree)
(car (undo-tree-node-next node))))
(decf (undo-tree-size buffer-undo-tree)
(+ (undo-list-byte-size (undo-tree-node-undo node))
(undo-list-byte-size (undo-tree-node-redo node))))
(decf (undo-tree-count buffer-undo-tree))
(setf (undo-tree-node-undo node) nil
(undo-tree-node-redo node) nil
(undo-tree-node-previous node) nil)
(if (or (> (length (undo-tree-node-next node)) 1)
(eq (car (undo-tree-node-next node))
(undo-tree-current buffer-undo-tree)))
(undo-tree-oldest-leaf node)
node)))
(let* ((parent (undo-tree-node-previous node))
(current (nth (undo-tree-node-branch parent)
(undo-tree-node-next parent))))
(let ((r (undo-tree-node-register node)))
(when (and r (eq (get-register r) node))
(set-register r nil)))
(decf (undo-tree-size buffer-undo-tree)
(+ (undo-list-byte-size (undo-tree-node-undo node))
(undo-list-byte-size (undo-tree-node-redo node))))
(decf (undo-tree-count buffer-undo-tree))
(setf (undo-tree-node-next parent)
(delq node (undo-tree-node-next parent))
(undo-tree-node-branch parent)
(undo-tree-position current (undo-tree-node-next parent)))
(if (or (eq parent (undo-tree-current buffer-undo-tree))
(and (undo-tree-node-next parent)
(or (not (eq parent (undo-tree-root buffer-undo-tree)))
(> (length (undo-tree-node-next parent)) 1))))
(undo-tree-oldest-leaf parent)
parent)))))
(defun undo-tree-discard-history ()
"Discard undo history until we're within memory usage limits
set by `undo-limit', `undo-strong-limit' and `undo-outer-limit'."
(when (> (undo-tree-size buffer-undo-tree) undo-limit)
(let ((node (if (> (length (undo-tree-node-next
(undo-tree-root buffer-undo-tree))) 1)
(undo-tree-oldest-leaf (undo-tree-root buffer-undo-tree))
(undo-tree-root buffer-undo-tree))))
(while (and node
(> (undo-tree-size buffer-undo-tree) undo-strong-limit))
(setq node (undo-tree-discard-node node)))
(while (and node
(> (undo-tree-size buffer-undo-tree) undo-limit)
(> (- (undo-tree-size buffer-undo-tree)
(if (eq node (undo-tree-root buffer-undo-tree))
(+ (undo-list-byte-size
(undo-tree-node-undo
(car (undo-tree-node-next node))))
(undo-list-byte-size
(undo-tree-node-redo
(car (undo-tree-node-next node)))))
(+ (undo-list-byte-size (undo-tree-node-undo node))
(undo-list-byte-size (undo-tree-node-redo node)))
))
undo-limit))
(setq node (undo-tree-discard-node node)))
(when (> (undo-tree-size buffer-undo-tree) undo-outer-limit)
(if undo-ask-before-discard
(when (yes-or-no-p
(format
"Buffer `%s' undo info is %d bytes long; discard it? "
(buffer-name) (undo-tree-size buffer-undo-tree)))
(setq buffer-undo-tree nil))
(display-warning
'(undo discard-info)
(concat
(format "Buffer `%s' undo info was %d bytes long.\n"
(buffer-name) (undo-tree-size buffer-undo-tree))
"The undo info was discarded because it exceeded\
`undo-outer-limit'.
This is normal if you executed a command that made a huge change
to the buffer. In that case, to prevent similar problems in the
future, set `undo-outer-limit' to a value that is large enough to
cover the maximum size of normal changes you expect a single
command to make, but not so large that it might exceed the
maximum memory allotted to Emacs.
If you did not execute any such command, the situation is
probably due to a bug and you should report it.
You can disable the popping up of this buffer by adding the entry
\(undo discard-info) to the user option `warning-suppress-types',
which is defined in the `warnings' library.\n")
:warning)
(setq buffer-undo-tree nil)))
)))
(defun undo-tree-compute-widths (node)
"Recursively compute widths for nodes below NODE."
(let ((stack (list node))
res)
(while stack
(if (undo-tree-node-p
(setq res (undo-tree-node-compute-widths (car stack))))
(push res stack)
(setf (undo-tree-node-lwidth (car stack)) (aref res 0)
(undo-tree-node-cwidth (car stack)) (aref res 1)
(undo-tree-node-rwidth (car stack)) (aref res 2))
(pop stack)))))
(defun undo-tree-node-compute-widths (node)
(let ((num-children (length (undo-tree-node-next node)))
(lwidth 0) (cwidth 0) (rwidth 0) p)
(catch 'need-widths
(cond
((= 0 num-children)
(setf cwidth 1
(undo-tree-node-lwidth node) 0
(undo-tree-node-cwidth node) 1
(undo-tree-node-rwidth node) 0))
((= (mod num-children 2) 1)
(setq p (undo-tree-node-next node))
(dotimes (i (/ num-children 2))
(if (undo-tree-node-lwidth (car p))
(incf lwidth (+ (undo-tree-node-lwidth (car p))
(undo-tree-node-cwidth (car p))
(undo-tree-node-rwidth (car p))))
(throw 'need-widths (car p)))
(setq p (cdr p)))
(if (undo-tree-node-lwidth (car p))
(incf lwidth (undo-tree-node-lwidth (car p)))
(throw 'need-widths (car p)))
(setf cwidth (undo-tree-node-cwidth (car p)))
(incf rwidth (undo-tree-node-rwidth (car p)))
(setq p (cdr p))
(dotimes (i (/ num-children 2))
(if (undo-tree-node-lwidth (car p))
(incf rwidth (+ (undo-tree-node-lwidth (car p))
(undo-tree-node-cwidth (car p))
(undo-tree-node-rwidth (car p))))
(throw 'need-widths (car p)))
(setq p (cdr p))))
(t
(setq p (undo-tree-node-next node))
(dotimes (i (/ num-children 2))
(if (undo-tree-node-lwidth (car p))
(incf lwidth (+ (undo-tree-node-lwidth (car p))
(undo-tree-node-cwidth (car p))
(undo-tree-node-rwidth (car p))))
(throw 'need-widths (car p)))
(setq p (cdr p)))
(setq cwidth 0)
(dotimes (i (/ num-children 2))
(if (undo-tree-node-lwidth (car p))
(incf rwidth (+ (undo-tree-node-lwidth (car p))
(undo-tree-node-cwidth (car p))
(undo-tree-node-rwidth (car p))))
(throw 'need-widths (car p)))
(setq p (cdr p)))))
(vector lwidth cwidth rwidth))))
(defun undo-tree-clear-visualizer-data (tree)
(undo-tree-mapc
(lambda (n) (undo-tree-node-clear-visualizer-data n))
(undo-tree-root tree)))
(defun undo-tree-node-unmodified-p (node &optional mtime)
(let (changeset ntime)
(setq 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)))))))
(and ntime
(or (null mtime)
(if (listp (cdr ntime))
(equal ntime mtime)
(and (= (car ntime) (car mtime))
(= (cdr ntime) (cadr mtime))))))))
(defvar undo-adjusted-markers nil)
(defun undo-tree-pull-undo-in-region-branch (start end)
(if (undo-tree-reverting-redo-in-region-p start end)
t
(let* ((region-changeset (list nil))
(r region-changeset)
(delta-list (list nil))
(d delta-list)
(node (undo-tree-current buffer-undo-tree))
(repeated-undo-in-region
(undo-tree-repeated-undo-in-region-p start end))
undo-adjusted-markers fragment splice original-fragment original-splice original-current
got-visible-elt undo-list elt)
(cond
(repeated-undo-in-region
(setq original-current node
fragment (car (undo-tree-node-next node))
splice node)
(let ((mark-active nil))
(while (= (length (undo-tree-node-next node)) 1)
(undo-tree-undo-1)
(setq fragment node
node (undo-tree-current buffer-undo-tree))))
(when (eq splice node) (setq splice nil))
(setf (undo-tree-node-next node)
(delq fragment (undo-tree-node-next node))
(undo-tree-node-previous fragment) nil
original-fragment fragment
original-splice node))
((undo-tree-node-next node)
(setq fragment (undo-tree-make-node nil nil)
splice fragment)
(while (setq node (nth (undo-tree-node-branch node)
(undo-tree-node-next node)))
(push (undo-tree-make-node
splice
(undo-copy-list (undo-tree-node-undo node))
(undo-copy-list (undo-tree-node-redo node)))
(undo-tree-node-next splice))
(setq splice (car (undo-tree-node-next splice))))
(setq fragment (car (undo-tree-node-next fragment))
splice nil
node (undo-tree-current buffer-undo-tree))))
(catch 'abort
(while (and (not got-visible-elt) node (undo-tree-node-undo node))
(setq undo-list (cons nil (undo-copy-list (undo-tree-node-undo node)))
elt (cadr undo-list))
(if fragment
(progn
(setq fragment (undo-tree-grow-backwards fragment undo-list))
(unless splice (setq splice fragment)))
(setq fragment (undo-tree-make-node nil undo-list))
(setq splice fragment))
(while elt
(cond
((undo-elt-in-region elt start end)
(when (and (consp elt)
(or (stringp (car elt)) (integerp (car elt))))
(setq got-visible-elt t))
(undo-tree-adjust-elements-to-elt splice elt)
(setcdr r (list (undo-tree-apply-deltas elt (cdr delta-list))))
(setq r (cdr r))
(setcdr undo-list (cddr undo-list)))
((and (consp elt) (eq (car elt) t))
(setcdr undo-list (cddr undo-list)))
((undo-elt-crosses-region elt start end)
(if got-visible-elt
(setq undo-list nil)
(setq region-changeset nil)
(throw 'abort t)))
(t
(let ((delta (undo-delta elt)))
(when (/= 0 (cdr delta))
(setcdr d (list delta))
(setq d (cdr d))))
(setq undo-list (cdr undo-list))))
(setq elt (cadr undo-list)))
(if (cadr (undo-tree-node-undo fragment))
(pop (undo-tree-node-undo fragment))
(when (eq splice fragment) (setq splice nil))
(setq fragment (car (undo-tree-node-next fragment))))
(setq node (undo-tree-node-previous node))))
(setq region-changeset (cdr region-changeset))
(if (null region-changeset)
(when original-current
(push original-fragment (undo-tree-node-next original-splice))
(setf (undo-tree-node-branch original-splice) 0
(undo-tree-node-previous original-fragment) original-splice)
(let ((mark-active nil))
(while (not (eq (undo-tree-current buffer-undo-tree)
original-current))
(undo-tree-redo-1)))
nil)
(let ((mark-active nil)
(current (undo-tree-current buffer-undo-tree)))
(while (not (eq (undo-tree-current buffer-undo-tree) node))
(undo-tree-undo-1))
(while (not (eq (undo-tree-current buffer-undo-tree) current))
(undo-tree-redo-1)))
(cond
((null fragment)
(setq fragment (undo-tree-make-node node region-changeset))
(push fragment (undo-tree-node-next node))
(setf (undo-tree-node-branch node) 0)
(setf (undo-tree-current buffer-undo-tree) fragment))
((null splice)
(setq fragment (undo-tree-grow-backwards fragment region-changeset))
(push fragment (undo-tree-node-next node))
(setf (undo-tree-node-branch node) 0
(undo-tree-node-previous fragment) node)
(setf (undo-tree-current buffer-undo-tree) fragment))
(t
(setf (undo-tree-node-previous fragment) node)
(push fragment (undo-tree-node-next node))
(setf (undo-tree-node-branch node) 0)
(when repeated-undo-in-region
(setf (undo-tree-current buffer-undo-tree)
(undo-tree-node-previous original-fragment))
(let ((mark-active nil))
(while (not (eq (undo-tree-current buffer-undo-tree) splice))
(undo-tree-redo-1 nil 'preserve-undo))))
(setq node (undo-tree-make-node nil region-changeset))
(undo-tree-splice-node node splice)
(setf (undo-tree-current buffer-undo-tree) node)))
(setq node (undo-tree-node-previous fragment))
(while (progn
(and (setq node (car (undo-tree-node-next node)))
(not (eq node original-fragment))
(incf (undo-tree-count buffer-undo-tree))
(incf (undo-tree-size buffer-undo-tree)
(+ (undo-list-byte-size (undo-tree-node-undo node))
(undo-list-byte-size (undo-tree-node-redo node)))))))
t) )))
(defun undo-tree-pull-redo-in-region-branch (start end)
(if (undo-tree-reverting-undo-in-region-p start end)
t
(let* ((region-changeset (list nil))
(r region-changeset)
(delta-list (list nil))
(d delta-list)
(node (undo-tree-current buffer-undo-tree))
(repeated-redo-in-region
(undo-tree-repeated-redo-in-region-p start end))
undo-adjusted-markers fragment splice got-visible-elt redo-list elt)
(cond
(repeated-redo-in-region
(when (setq fragment (car (undo-tree-node-next node)))
(setf (undo-tree-node-previous fragment) nil
(undo-tree-node-next node)
(delq fragment (undo-tree-node-next node)))))
((undo-tree-node-next node)
(setq fragment (undo-tree-make-node nil nil)
splice fragment)
(while (setq node (nth (undo-tree-node-branch node)
(undo-tree-node-next node)))
(push (undo-tree-make-node
splice nil
(undo-copy-list (undo-tree-node-redo node)))
(undo-tree-node-next splice))
(setq splice (car (undo-tree-node-next splice))))
(setq fragment (car (undo-tree-node-next fragment)))))
(setq node fragment)
(catch 'abort
(while (and (not got-visible-elt) node (undo-tree-node-redo node))
(setq redo-list (push nil (undo-tree-node-redo node))
elt (cadr redo-list))
(while elt
(cond
((undo-elt-in-region elt start end)
(when (and (consp elt)
(or (stringp (car elt)) (integerp (car elt))))
(setq got-visible-elt t))
(undo-tree-adjust-elements-to-elt fragment elt t)
(setcdr r (list (undo-tree-apply-deltas elt (cdr delta-list) -1)))
(setq r (cdr r))
(setcdr redo-list (cddr redo-list)))
((and (consp elt) (eq (car elt) t))
(setcdr redo-list (cddr redo-list)))
((undo-elt-crosses-region elt start end)
(if got-visible-elt
(setq redo-list nil)
(setq region-changeset nil)
(throw 'abort t)))
(t
(let ((delta (undo-delta elt)))
(when (/= 0 (cdr delta))
(setcdr d (list delta))
(setq d (cdr d))))
(setq redo-list (cdr redo-list))))
(setq elt (cadr redo-list)))
(if (cadr (undo-tree-node-redo node))
(pop (undo-tree-node-undo node))
(if (eq fragment node)
(setq fragment (car (undo-tree-node-next fragment)))
(undo-tree-snip-node node)))
(setq node (car (undo-tree-node-next node)))))
(setq region-changeset (cdr region-changeset))
(setq node (undo-tree-current buffer-undo-tree))
(if (null (car region-changeset))
(when (and repeated-redo-in-region fragment)
(push fragment (undo-tree-node-next node))
(setf (undo-tree-node-branch node) 0
(undo-tree-node-previous fragment) node)
nil)
(setq fragment
(if fragment
(undo-tree-grow-backwards fragment nil region-changeset)
(undo-tree-make-node nil nil region-changeset)))
(push fragment (undo-tree-node-next node))
(setf (undo-tree-node-branch node) 0
(undo-tree-node-previous fragment) node)
(unless repeated-redo-in-region
(setq node fragment)
(while (and (setq node (car (undo-tree-node-next node)))
(incf (undo-tree-count buffer-undo-tree))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size
(undo-tree-node-redo node))))))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-redo fragment)))
t) )))
(defun undo-tree-adjust-elements-to-elt (node undo-elt &optional below)
"Adjust buffer positions of undo elements, starting at NODE's
and going up the tree (or down the active branch if BELOW is
non-nil) and through the nodes' undo elements until we reach
UNDO-ELT. UNDO-ELT must appear somewhere in the undo changeset
of either NODE itself or some node above it in the tree."
(let ((delta (list (undo-delta undo-elt)))
(undo-list (undo-tree-node-undo node)))
(while (and (car undo-list)
(not (eq (car undo-list) undo-elt)))
(setcar undo-list
(undo-tree-apply-deltas (car undo-list) delta -1))
(unless (car (setq undo-list (cdr undo-list)))
(if below
(setq node (nth (undo-tree-node-branch node)
(undo-tree-node-next node)))
(setq node (undo-tree-node-previous node)))
(setq undo-list (undo-tree-node-undo node))))))
(defun undo-tree-apply-deltas (undo-elt deltas &optional sgn)
(let (position offset)
(dolist (delta deltas)
(setq position (car delta)
offset (* (cdr delta) (or sgn 1)))
(cond
((integerp undo-elt)
(when (>= undo-elt position)
(setq undo-elt (- undo-elt offset))))
((atom undo-elt))
((stringp (car undo-elt))
(let ((text-pos (abs (cdr undo-elt)))
(point-at-end (< (cdr undo-elt) 0)))
(if (>= text-pos position)
(setcdr undo-elt (* (if point-at-end -1 1)
(- text-pos offset))))))
((integerp (car undo-elt))
(when (>= (car undo-elt) position)
(setcar undo-elt (- (car undo-elt) offset))
(setcdr undo-elt (- (cdr undo-elt) offset))))
((null (car undo-elt))
(let ((tail (nthcdr 3 undo-elt)))
(when (>= (car tail) position)
(setcar tail (- (car tail) offset))
(setcdr tail (- (cdr tail) offset)))))
))
undo-elt))
(defun undo-tree-repeated-undo-in-region-p (start end)
(let ((node (undo-tree-current buffer-undo-tree)))
(and (setq node
(nth (undo-tree-node-branch node) (undo-tree-node-next node)))
(eq (undo-tree-node-undo-beginning node) start)
(eq (undo-tree-node-undo-end node) end))))
(defun undo-tree-repeated-redo-in-region-p (start end)
(let ((node (undo-tree-current buffer-undo-tree)))
(and (eq (undo-tree-node-redo-beginning node) start)
(eq (undo-tree-node-redo-end node) end))))
(defalias 'undo-tree-reverting-undo-in-region-p
'undo-tree-repeated-undo-in-region-p)
(defalias 'undo-tree-reverting-redo-in-region-p
'undo-tree-repeated-redo-in-region-p)
(define-minor-mode undo-tree-mode
"Toggle undo-tree mode.
With no argument, this command toggles the mode.
A positive prefix argument turns the mode on.
A negative prefix argument turns it off.
Undo-tree-mode replaces Emacs' standard undo feature with a more
powerful yet easier to use version, that treats the undo history
as what it is: a tree.
The following keys are available in `undo-tree-mode':
\\{undo-tree-map}
Within the undo-tree visualizer, the following keys are available:
\\{undo-tree-visualizer-mode-map}"
nil undo-tree-mode-lighter undo-tree-map
(when (not undo-tree-mode)
(undo-list-rebuild-from-tree)
(setq buffer-undo-tree nil)))
(defun turn-on-undo-tree-mode (&optional print-message)
"Enable `undo-tree-mode' in the current buffer, when appropriate.
Some major modes implement their own undo system, which should
not normally be overridden by `undo-tree-mode'. This command does
not enable `undo-tree-mode' in such buffers. If you want to force
`undo-tree-mode' to be enabled regardless, use (undo-tree-mode 1)
instead.
The heuristic used to detect major modes in which
`undo-tree-mode' should not be used is to check whether either
the `undo' command has been remapped, or the default undo
keybindings (C-/ and C-_) have been overridden somewhere other
than in the global map. In addition, `undo-tree-mode' will not be
enabled if the buffer's `major-mode' appears in
`undo-tree-incompatible-major-modes'."
(interactive "p")
(if (or (key-binding [remap undo])
(undo-tree-overridden-undo-bindings-p)
(memq major-mode undo-tree-incompatible-major-modes))
(when print-message
(message "Buffer does not support undo-tree-mode;\
undo-tree-mode NOT enabled"))
(undo-tree-mode 1)))
(defun undo-tree-overridden-undo-bindings-p ()
"Returns t if default undo bindings are overridden, nil otherwise.
Checks if either of the default undo key bindings (\"C-/\" or
\"C-_\") are overridden in the current buffer by any keymap other
than the global one. (So global redefinitions of the default undo
key bindings do not count.)"
(let ((binding1 (lookup-key (current-global-map) [?\C-/]))
(binding2 (lookup-key (current-global-map) [?\C-_])))
(global-set-key [?\C-/] 'undo)
(global-set-key [?\C-_] 'undo)
(unwind-protect
(or (and (key-binding [?\C-/])
(not (eq (key-binding [?\C-/]) 'undo)))
(and (key-binding [?\C-_])
(not (eq (key-binding [?\C-_]) 'undo))))
(global-set-key [?\C-/] binding1)
(global-set-key [?\C-_] binding2))))
(define-globalized-minor-mode global-undo-tree-mode
undo-tree-mode turn-on-undo-tree-mode)
(defun undo-tree-undo (&optional arg)
"Undo changes.
Repeat this command to undo more changes.
A numeric ARG serves as a repeat count.
In Transient Mark mode when the mark is active, only undo changes
within the current region. Similarly, when not in Transient Mark
mode, just \\[universal-argument] as an argument limits undo to
changes within the current region."
(interactive "*P")
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(when (eq buffer-undo-list t)
(user-error "No undo information in this buffer"))
(undo-tree-undo-1 arg)
(when (> (undo-tree-num-branches) 1) (message "Undo branch point!")))
(defun undo-tree-undo-1 (&optional arg preserve-redo preserve-timestamps)
(let ((undo-in-progress t)
(undo-in-region (and undo-tree-enable-undo-in-region
(or (region-active-p)
(and arg (not (numberp arg))))))
pos current)
(undo-list-transfer-to-tree)
(dotimes (i (or (and (numberp arg) (prefix-numeric-value arg)) 1))
(unless (undo-tree-node-previous (undo-tree-current buffer-undo-tree))
(user-error "No further undo information"))
(when (and undo-in-region
(not (undo-tree-pull-undo-in-region-branch
(region-beginning) (region-end))))
(user-error "No further undo information for region"))
(setq current (undo-tree-current buffer-undo-tree))
(decf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-undo current)))
(setf (undo-tree-node-undo current)
(undo-list-clean-GCd-elts (undo-tree-node-undo current)))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-undo current)))
(when undo-in-region
(setq pos (set-marker (make-marker) (point)))
(set-marker-insertion-type pos t))
(primitive-undo 1 (undo-tree-copy-list (undo-tree-node-undo current)))
(undo-boundary)
(if preserve-redo
(progn
(undo-list-pop-changeset)
(decf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-redo current)))
(setf (undo-tree-node-redo current)
(undo-list-clean-GCd-elts (undo-tree-node-redo current)))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-redo current))))
(decf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-redo current)))
(setf (undo-tree-node-redo current)
(undo-list-pop-changeset 'discard-pos))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-redo current))))
(setf (undo-tree-current buffer-undo-tree)
(undo-tree-node-previous (undo-tree-current buffer-undo-tree)))
(unless preserve-timestamps
(setf (undo-tree-node-timestamp (undo-tree-current buffer-undo-tree))
(current-time)))
(if (not undo-in-region)
(undo-tree-node-clear-region-data current)
(goto-char pos)
(setf (undo-tree-node-undo-beginning current) (region-beginning)
(undo-tree-node-undo-end current) (region-end))
(set-marker pos nil)))
(setq deactivate-mark (not undo-in-region))))
(defun undo-tree-redo (&optional arg)
"Redo changes. A numeric ARG serves as a repeat count.
In Transient Mark mode when the mark is active, only redo changes
within the current region. Similarly, when not in Transient Mark
mode, just \\[universal-argument] as an argument limits redo to
changes within the current region."
(interactive "*P")
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(when (eq buffer-undo-list t)
(user-error "No undo information in this buffer"))
(undo-tree-redo-1 arg)
(when (> (undo-tree-num-branches) 1) (message "Undo branch point!")))
(defun undo-tree-redo-1 (&optional arg preserve-undo preserve-timestamps)
(let ((undo-in-progress t)
(redo-in-region (and undo-tree-enable-undo-in-region
(or (region-active-p)
(and arg (not (numberp arg))))))
pos current)
(undo-list-transfer-to-tree)
(dotimes (i (or (and (numberp arg) (prefix-numeric-value arg)) 1))
(when (null (undo-tree-node-next (undo-tree-current buffer-undo-tree)))
(user-error "No further redo information"))
(when (and redo-in-region
(not (undo-tree-pull-redo-in-region-branch
(region-beginning) (region-end))))
(user-error "No further redo information for region"))
(setq current (undo-tree-current buffer-undo-tree)
current (nth (undo-tree-node-branch current)
(undo-tree-node-next current)))
(decf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-redo current)))
(setf (undo-tree-node-redo current)
(undo-list-clean-GCd-elts (undo-tree-node-redo current)))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-redo current)))
(when redo-in-region
(setq pos (set-marker (make-marker) (point)))
(set-marker-insertion-type pos t))
(primitive-undo 1 (undo-tree-copy-list (undo-tree-node-redo current)))
(undo-boundary)
(setf (undo-tree-current buffer-undo-tree) current)
(if preserve-undo
(progn
(undo-list-pop-changeset)
(decf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-undo current)))
(setf (undo-tree-node-undo current)
(undo-list-clean-GCd-elts (undo-tree-node-undo current)))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-undo current))))
(decf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-undo current)))
(setf (undo-tree-node-undo current)
(undo-list-pop-changeset 'discard-pos))
(incf (undo-tree-size buffer-undo-tree)
(undo-list-byte-size (undo-tree-node-undo current))))
(unless preserve-timestamps
(setf (undo-tree-node-timestamp current) (current-time)))
(if (not redo-in-region)
(undo-tree-node-clear-region-data current)
(goto-char pos)
(setf (undo-tree-node-redo-beginning current) (region-beginning)
(undo-tree-node-redo-end current) (region-end))
(set-marker pos nil)))
(setq deactivate-mark (not redo-in-region))))
(defun undo-tree-switch-branch (branch)
"Switch to a different BRANCH of the undo tree.
This will affect which branch to descend when *redoing* changes
using `undo-tree-redo'."
(interactive (list (or (and prefix-arg (prefix-numeric-value prefix-arg))
(and (not (eq buffer-undo-list t))
(or (undo-list-transfer-to-tree) t)
(let ((b (undo-tree-node-branch
(undo-tree-current
buffer-undo-tree))))
(cond
((= (undo-tree-num-branches) 2) (- 1 b))
((> (undo-tree-num-branches) 2)
(read-number
(format "Branch (0-%d, on %d): "
(1- (undo-tree-num-branches)) b)))
))))))
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(when (eq buffer-undo-list t)
(user-error "No undo information in this buffer"))
(when (<= (undo-tree-num-branches) 1)
(user-error "Not at undo branch point"))
(when (or (< branch 0) (> branch (1- (undo-tree-num-branches))))
(user-error "Invalid branch number"))
(undo-list-transfer-to-tree)
(setf (undo-tree-node-branch (undo-tree-current buffer-undo-tree))
branch)
(message "Switched to branch %d" branch))
(defun undo-tree-set (node &optional preserve-timestamps)
(let ((path (make-hash-table :test 'eq))
(n node))
(puthash (undo-tree-root buffer-undo-tree) t path)
(while (progn
(puthash n t path)
(when (undo-tree-node-previous n)
(setf (undo-tree-node-branch (undo-tree-node-previous n))
(undo-tree-position
n (undo-tree-node-next (undo-tree-node-previous n))))
(setq n (undo-tree-node-previous n)))))
(setq n (undo-tree-current buffer-undo-tree))
(while (not (gethash n path))
(setq n (undo-tree-node-previous n)))
(while (not (eq (undo-tree-current buffer-undo-tree) n))
(undo-tree-undo-1 nil nil preserve-timestamps))
(while (not (eq (undo-tree-current buffer-undo-tree) node))
(undo-tree-redo-1 nil nil preserve-timestamps))
n))
(defun undo-tree-save-state-to-register (register)
"Store current undo-tree state to REGISTER.
The saved state can be restored using
`undo-tree-restore-state-from-register'.
Argument is a character, naming the register."
(interactive "cUndo-tree state to register: ")
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(when (eq buffer-undo-list t)
(user-error "No undo information in this buffer"))
(undo-list-transfer-to-tree)
(set-register
register (registerv-make
(undo-tree-make-register-data
(current-buffer) (undo-tree-current buffer-undo-tree))
:print-func 'undo-tree-register-data-print-func))
(setf (undo-tree-node-register (undo-tree-current buffer-undo-tree))
register))
(defun undo-tree-restore-state-from-register (register)
"Restore undo-tree state from REGISTER.
The state must be saved using `undo-tree-save-state-to-register'.
Argument is a character, naming the register."
(interactive "*cRestore undo-tree state from register: ")
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(let ((data (registerv-data (get-register register))))
(cond
((eq buffer-undo-list t)
(user-error "No undo information in this buffer"))
((not (undo-tree-register-data-p data))
(user-error "Register doesn't contain undo-tree state"))
((not (eq (current-buffer) (undo-tree-register-data-buffer data)))
(user-error "Register contains undo-tree state for a different buffer")))
(undo-list-transfer-to-tree)
(undo-tree-set (undo-tree-register-data-node data))))
(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
\".~undo-tree~\" appended.
A match for FILE is sought in `undo-tree-history-directory-alist'
\(see the documentation of that variable for details\). If the
directory for the backup doesn't exist, it is created."
(let* ((backup-directory-alist undo-tree-history-directory-alist)
(name (make-backup-file-name-1 file)))
(concat (file-name-directory name) "." (file-name-nondirectory name)
".~undo-tree~")))
(defun undo-tree-save-history (&optional filename overwrite)
"Store undo-tree history to file.
If optional argument FILENAME is omitted, default save file is
\".<buffer-file-name>.~undo-tree\" if buffer is visiting a file.
Otherwise, prompt for one.
If OVERWRITE is non-nil, any existing file will be overwritten
without asking for confirmation."
(interactive)
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(when (eq buffer-undo-list t)
(user-error "No undo information in this buffer"))
(undo-list-transfer-to-tree)
(when (and buffer-undo-tree (not (eq buffer-undo-tree t)))
(condition-case nil
(undo-tree-kill-visualizer)
(error (undo-tree-clear-visualizer-data buffer-undo-tree)))
(let ((buff (current-buffer))
tree)
(unless filename
(setq filename
(if buffer-file-name
(undo-tree-make-history-save-file-name buffer-file-name)
(expand-file-name (read-file-name "File to save in: ") nil))))
(when (or (not (file-exists-p filename))
overwrite
(yes-or-no-p (format "Overwrite \"%s\"? " filename)))
(unwind-protect
(progn
(undo-tree-decircle buffer-undo-tree)
(setq tree (copy-undo-tree buffer-undo-tree))
(setf (undo-tree-object-pool tree) nil)
(with-auto-compression-mode
(with-temp-buffer
(prin1 (sha1 buff) (current-buffer))
(terpri (current-buffer))
(let ((print-circle t)) (prin1 tree (current-buffer)))
(write-region nil nil filename))))
(undo-tree-recircle buffer-undo-tree))
))))
(defun undo-tree-load-history (&optional filename noerror)
"Load undo-tree history from file.
If optional argument FILENAME is null, default load file is
\".<buffer-file-name>.~undo-tree\" if buffer is visiting a file.
Otherwise, prompt for one.
If optional argument NOERROR is non-nil, return nil instead of
signaling an error if file is not found."
(interactive)
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(unless filename
(setq filename
(if buffer-file-name
(undo-tree-make-history-save-file-name buffer-file-name)
(expand-file-name (read-file-name "File to load from: ") nil))))
(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
(with-temp-buffer
(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)))
(setf (undo-tree-object-pool tree)
(make-hash-table :test 'eq :weakness 'value))
(undo-tree-recircle tree)
(setq buffer-undo-tree tree))))
(defun undo-tree-save-history-hook ()
(when (and undo-tree-mode undo-tree-auto-save-history
(not (eq buffer-undo-list t)))
(undo-tree-save-history nil t) nil))
(defun undo-tree-load-history-hook ()
(when (and undo-tree-mode undo-tree-auto-save-history
(not (eq buffer-undo-list t))
(not revert-buffer-in-progress-p))
(undo-tree-load-history nil t)))
(defun undo-tree-visualize ()
"Visualize the current buffer's undo tree."
(interactive "*")
(unless undo-tree-mode
(user-error "Undo-tree mode not enabled in buffer"))
(deactivate-mark)
(when (eq buffer-undo-list t)
(user-error "No undo information in this buffer"))
(undo-list-transfer-to-tree)
(add-hook 'before-change-functions 'undo-tree-kill-visualizer nil t)
(let ((undo-tree buffer-undo-tree)
(buff (current-buffer))
(display-buffer-mark-dedicated 'soft))
(switch-to-buffer-other-window
(get-buffer-create undo-tree-visualizer-buffer-name))
(setq undo-tree-visualizer-parent-buffer buff)
(setq undo-tree-visualizer-parent-mtime
(and (buffer-file-name buff)
(nth 5 (file-attributes (buffer-file-name buff)))))
(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)
(setq buffer-undo-tree undo-tree)
(set (make-local-variable 'undo-tree-visualizer-lazy-drawing)
(or (eq undo-tree-visualizer-lazy-drawing t)
(and (numberp undo-tree-visualizer-lazy-drawing)
(>= (undo-tree-count undo-tree)
undo-tree-visualizer-lazy-drawing))))
(when undo-tree-visualizer-diff (undo-tree-visualizer-show-diff))
(let ((inhibit-read-only t)) (undo-tree-draw-tree undo-tree))))
(defun undo-tree-kill-visualizer (&rest _dummy)
(unless (or undo-tree-inhibit-kill-visualizer
(null (get-buffer undo-tree-visualizer-buffer-name)))
(with-current-buffer undo-tree-visualizer-buffer-name
(undo-tree-visualizer-quit))))
(defun undo-tree-draw-tree (undo-tree)
(let ((node (if undo-tree-visualizer-lazy-drawing
(undo-tree-current undo-tree)
(undo-tree-root undo-tree))))
(erase-buffer)
(setq undo-tree-visualizer-needs-extending-down nil
undo-tree-visualizer-needs-extending-up nil)
(undo-tree-clear-visualizer-data undo-tree)
(undo-tree-compute-widths node)
(if undo-tree-visualizer-lazy-drawing
(progn
(undo-tree-move-down (/ (window-height) 2))
(undo-tree-move-forward (max 2 (/ (window-width) 4)))) (undo-tree-move-down 1) (undo-tree-move-forward
(max (/ (window-width) 2)
(+ (undo-tree-node-char-lwidth node)
(if undo-tree-visualizer-timestamps
(/ (- undo-tree-visualizer-spacing 4) 2)
0)
2)))) (setf (undo-tree-node-marker node) (make-marker))
(set-marker-insertion-type (undo-tree-node-marker node) nil)
(move-marker (undo-tree-node-marker node) (point))
(let ((undo-tree-insert-face 'undo-tree-visualizer-default-face)
node-list)
(if (not undo-tree-visualizer-lazy-drawing)
(undo-tree-extend-down node t)
(undo-tree-extend-down node)
(undo-tree-extend-up node)
(setq node-list undo-tree-visualizer-needs-extending-down
undo-tree-visualizer-needs-extending-down nil)
(while node-list (undo-tree-extend-down (pop node-list)))))
(let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face))
(undo-tree-highlight-active-branch
(or undo-tree-visualizer-needs-extending-up
(undo-tree-root undo-tree))))
(undo-tree-draw-node (undo-tree-current undo-tree) 'current)))
(defun undo-tree-extend-down (node &optional bottom)
(let ((extended nil)
(cur-stack (list node))
next-stack)
(unless (and (undo-tree-node-p bottom) (undo-tree-node-marker bottom))
(while (or cur-stack
(prog1 (setq cur-stack next-stack)
(setq next-stack nil)))
(setq node (pop cur-stack))
(if (or (eq bottom t)
(and (undo-tree-node-p bottom)
(not (eq (undo-tree-node-previous node) bottom)))
(and (integerp bottom)
(>= bottom (line-number-at-pos
(undo-tree-node-marker node))))
(and (null bottom)
(pos-visible-in-window-p (undo-tree-node-marker node)
nil t)))
(progn
(unless (and (undo-tree-node-next node)
(undo-tree-node-marker
(nth (undo-tree-node-branch node)
(undo-tree-node-next node))))
(goto-char (undo-tree-node-marker node))
(undo-tree-draw-subtree node)
(setq extended t))
(setq next-stack
(append (undo-tree-node-next node) next-stack)))
(push node undo-tree-visualizer-needs-extending-down))))
extended))
(defun undo-tree-extend-up (node &optional top)
(let ((extended nil) parent)
(unless (and (undo-tree-node-p top) (undo-tree-node-marker top))
(while node
(setq parent (undo-tree-node-previous node))
(if parent
(if (or (eq top t)
(and (undo-tree-node-p top) (not (eq node top)))
(and (integerp top)
(< top (line-number-at-pos
(undo-tree-node-marker node))))
(and (null top)
(< (min (line-number-at-pos (point))
(line-number-at-pos (window-start)))
(line-number-at-pos
(undo-tree-node-marker node)))))
(when (not (undo-tree-node-marker parent))
(undo-tree-compute-widths parent)
(undo-tree-move-to-parent node)
(setf (undo-tree-node-marker parent) (make-marker))
(set-marker-insertion-type
(undo-tree-node-marker parent) nil)
(move-marker (undo-tree-node-marker parent) (point))
(setq undo-tree-visualizer-needs-extending-down
(nconc (delq node (undo-tree-draw-subtree parent))
undo-tree-visualizer-needs-extending-down))
(setq extended t))
(setq undo-tree-visualizer-needs-extending-up (when parent node)
parent nil))
(setq undo-tree-visualizer-needs-extending-up nil)
(goto-char (undo-tree-node-marker node))
(undo-tree-move-up 1) (delete-region (point-min) (line-beginning-position)))
(setq node parent)))
extended))
(defun undo-tree-expand-down (from &optional to)
(when undo-tree-visualizer-needs-extending-down
(let ((inhibit-read-only t)
node-list extended)
(when to
(setq extended (undo-tree-extend-down from to))
(goto-char (undo-tree-node-marker to))
(redisplay t)) (setq node-list undo-tree-visualizer-needs-extending-down
undo-tree-visualizer-needs-extending-down nil)
(when node-list
(dolist (n node-list)
(when (undo-tree-extend-down n) (setq extended t)))
(when extended
(let ((undo-tree-insert-face
'undo-tree-visualizer-active-branch-face))
(undo-tree-highlight-active-branch from)))))))
(defun undo-tree-expand-up (from &optional to)
(when undo-tree-visualizer-needs-extending-up
(let ((inhibit-read-only t)
extended node-list)
(when to
(setq extended (undo-tree-extend-up from to))
(goto-char (undo-tree-node-marker to))
(when (<= (line-number-at-pos (point)) scroll-margin)
(undo-tree-move-up (if (= scroll-conservatively 0)
(/ (window-height) 2) 3))
(when (undo-tree-extend-up to) (setq extended t))
(goto-char (undo-tree-node-marker to))
(unless (= scroll-conservatively 0) (recenter scroll-margin))))
(and undo-tree-visualizer-needs-extending-up
(undo-tree-extend-up undo-tree-visualizer-needs-extending-up)
(setq extended t))
(setq node-list undo-tree-visualizer-needs-extending-down
undo-tree-visualizer-needs-extending-down nil)
(dolist (n node-list) (undo-tree-extend-down n))
(when extended
(let ((undo-tree-insert-face
'undo-tree-visualizer-active-branch-face))
(undo-tree-highlight-active-branch
(or undo-tree-visualizer-needs-extending-up
(undo-tree-root buffer-undo-tree))
from))))))
(defun undo-tree-highlight-active-branch (node &optional end)
(let ((stack (list node)))
(while stack
(setq node (pop stack))
(unless (or (eq node end)
(memq node undo-tree-visualizer-needs-extending-down))
(goto-char (undo-tree-node-marker node))
(setq node (undo-tree-draw-subtree node 'active)
stack (nconc stack node))))))
(defun undo-tree-draw-node (node &optional current)
(goto-char (undo-tree-node-marker node))
(when undo-tree-visualizer-timestamps
(undo-tree-move-backward (/ undo-tree-visualizer-spacing 2)))
(let* ((undo-tree-insert-face (and undo-tree-insert-face
(or (and (consp undo-tree-insert-face)
undo-tree-insert-face)
(list undo-tree-insert-face))))
(register (undo-tree-node-register node))
(unmodified (if undo-tree-visualizer-parent-mtime
(undo-tree-node-unmodified-p
node undo-tree-visualizer-parent-mtime)
(undo-tree-node-unmodified-p node)))
node-string)
(unless (and register
(undo-tree-register-data-p
(registerv-data (get-register register)))
(eq node (undo-tree-register-data-node
(registerv-data (get-register register)))))
(setq register nil))
(setq node-string
(cond
(undo-tree-visualizer-timestamps
(undo-tree-timestamp-to-string
(undo-tree-node-timestamp node)
undo-tree-visualizer-relative-timestamps
current register))
(register (char-to-string register))
(unmodified "s")
(current "x")
(t "o"))
undo-tree-insert-face
(nconc
(cond
(current '(undo-tree-visualizer-current-face))
(unmodified '(undo-tree-visualizer-unmodified-face))
(register '(undo-tree-visualizer-register-face)))
undo-tree-insert-face))
(undo-tree-insert node-string)
(undo-tree-move-backward (if undo-tree-visualizer-timestamps
(1+ (/ undo-tree-visualizer-spacing 2))
1))
(move-marker (undo-tree-node-marker node) (point))
(put-text-property (point) (1+ (point)) 'undo-tree-node node)))
(defun undo-tree-draw-subtree (node &optional active-branch)
(let ((num-children (length (undo-tree-node-next node)))
node-list pos trunk-pos n)
(undo-tree-draw-node node)
(cond
((= num-children 0))
((= num-children 1)
(undo-tree-move-down 1)
(undo-tree-insert ?|)
(undo-tree-move-backward 1)
(undo-tree-move-down 1)
(undo-tree-insert ?|)
(undo-tree-move-backward 1)
(undo-tree-move-down 1)
(setq n (car (undo-tree-node-next node)))
(unless (markerp (undo-tree-node-marker n))
(setf (undo-tree-node-marker n) (make-marker))
(set-marker-insertion-type (undo-tree-node-marker n) nil))
(move-marker (undo-tree-node-marker n) (point))
(push n node-list))
(t
(undo-tree-move-down 1)
(undo-tree-insert ?|)
(undo-tree-move-backward 1)
(move-marker (setq trunk-pos (make-marker)) (point))
(undo-tree-move-backward
(- (undo-tree-node-char-lwidth node)
(undo-tree-node-char-lwidth
(car (undo-tree-node-next node)))))
(move-marker (setq pos (make-marker)) (point))
(setq n (cons nil (undo-tree-node-next node)))
(dotimes (i (/ num-children 2))
(setq n (cdr n))
(when (or (null active-branch)
(eq (car n)
(nth (undo-tree-node-branch node)
(undo-tree-node-next node))))
(undo-tree-move-forward 2)
(undo-tree-insert ?_ (- trunk-pos pos 2))
(goto-char pos)
(undo-tree-move-forward 1)
(undo-tree-move-down 1)
(undo-tree-insert ?/)
(undo-tree-move-backward 2)
(undo-tree-move-down 1)
(unless (markerp (undo-tree-node-marker (car n)))
(setf (undo-tree-node-marker (car n)) (make-marker))
(set-marker-insertion-type (undo-tree-node-marker (car n)) nil))
(move-marker (undo-tree-node-marker (car n)) (point))
(push (car n) node-list))
(goto-char pos)
(undo-tree-move-forward
(+ (undo-tree-node-char-rwidth (car n))
(undo-tree-node-char-lwidth (cadr n))
undo-tree-visualizer-spacing 1))
(move-marker pos (point)))
(when (= (mod num-children 2) 1)
(setq n (cdr n))
(when (or (null active-branch)
(eq (car n)
(nth (undo-tree-node-branch node)
(undo-tree-node-next node))))
(undo-tree-move-down 1)
(undo-tree-insert ?|)
(undo-tree-move-backward 1)
(undo-tree-move-down 1)
(unless (markerp (undo-tree-node-marker (car n)))
(setf (undo-tree-node-marker (car n)) (make-marker))
(set-marker-insertion-type (undo-tree-node-marker (car n)) nil))
(move-marker (undo-tree-node-marker (car n)) (point))
(push (car n) node-list))
(goto-char pos)
(undo-tree-move-forward
(+ (undo-tree-node-char-rwidth (car n))
(if (cadr n) (undo-tree-node-char-lwidth (cadr n)) 0)
undo-tree-visualizer-spacing 1))
(move-marker pos (point)))
(move-marker trunk-pos (1+ trunk-pos))
(dotimes (i (/ num-children 2))
(setq n (cdr n))
(when (or (null active-branch)
(eq (car n)
(nth (undo-tree-node-branch node)
(undo-tree-node-next node))))
(goto-char trunk-pos)
(undo-tree-insert ?_ (- pos trunk-pos 1))
(goto-char pos)
(undo-tree-move-backward 1)
(undo-tree-move-down 1)
(undo-tree-insert ?\\)
(undo-tree-move-down 1)
(unless (markerp (undo-tree-node-marker (car n)))
(setf (undo-tree-node-marker (car n)) (make-marker))
(set-marker-insertion-type (undo-tree-node-marker (car n)) nil))
(move-marker (undo-tree-node-marker (car n)) (point))
(push (car n) node-list))
(when (cdr n)
(goto-char pos)
(undo-tree-move-forward
(+ (undo-tree-node-char-rwidth (car n))
(if (cadr n) (undo-tree-node-char-lwidth (cadr n)) 0)
undo-tree-visualizer-spacing 1))
(move-marker pos (point))))
))
(nreverse node-list)))
(defun undo-tree-node-char-lwidth (node)
(if (= (length (undo-tree-node-next node)) 0) 0
(- (* (+ undo-tree-visualizer-spacing 1) (undo-tree-node-lwidth node))
(if (= (undo-tree-node-cwidth node) 0)
(1+ (/ undo-tree-visualizer-spacing 2)) 0))))
(defun undo-tree-node-char-rwidth (node)
(if (= (length (undo-tree-node-next node)) 0) 0
(- (* (+ undo-tree-visualizer-spacing 1) (undo-tree-node-rwidth node))
(if (= (undo-tree-node-cwidth node) 0)
(1+ (/ undo-tree-visualizer-spacing 2)) 0))))
(defun undo-tree-insert (str &optional arg)
(unless arg (setq arg 1))
(when (characterp str)
(setq str (make-string arg str))
(setq arg 1))
(dotimes (i arg) (insert str))
(setq arg (* arg (length str)))
(undo-tree-move-forward arg)
(setq mark-active nil)
(backward-delete-char arg)
(when undo-tree-insert-face
(put-text-property (- (point) arg) (point) 'face undo-tree-insert-face)))
(defun undo-tree-move-down (&optional arg)
(let ((row (line-number-at-pos))
(col (current-column))
line)
(unless arg (setq arg 1))
(forward-line arg)
(setq line (line-number-at-pos))
(when (/= line (+ row arg))
(cond
((< arg 0)
(insert (make-string (- line row arg) ?\n))
(forward-line (+ arg (- row line))))
(t (insert (make-string (- arg (- line row)) ?\n)))))
(undo-tree-move-forward col)))
(defun undo-tree-move-up (&optional arg)
(unless arg (setq arg 1))
(undo-tree-move-down (- arg)))
(defun undo-tree-move-forward (&optional arg)
(unless arg (setq arg 1))
(let (n)
(cond
((>= arg 0)
(setq n (- (line-end-position) (point)))
(if (> n arg)
(forward-char arg)
(end-of-line)
(insert (make-string (- arg n) ? ))))
((< arg 0)
(setq arg (- arg))
(setq n (- (point) (line-beginning-position)))
(when (< (- n 2) arg) (let ((pos (move-marker (make-marker) (point))))
(set-marker-insertion-type pos t)
(goto-char (point-min))
(while (not (eobp))
(insert-before-markers (make-string (- arg -2 n) ? ))
(forward-line 1))
(goto-char pos)))
(backward-char arg)))))
(defun undo-tree-move-backward (&optional arg)
(unless arg (setq arg 1))
(undo-tree-move-forward (- arg)))
(defun undo-tree-move-to-parent (node)
(let* ((parent (undo-tree-node-previous node))
(n (undo-tree-node-next parent))
(l (length n)) p)
(goto-char (undo-tree-node-marker node))
(unless (= l 1)
(setq p (undo-tree-position node n))
(cond
((and (= (mod l 2) 1) (= p (/ l 2))))
((< p (/ l 2))
(setq n (nthcdr p n))
(undo-tree-move-forward
(+ (undo-tree-node-char-rwidth (car n))
(/ undo-tree-visualizer-spacing 2) 1))
(dotimes (i (- (/ l 2) p 1))
(setq n (cdr n))
(undo-tree-move-forward
(+ (undo-tree-node-char-lwidth (car n))
(undo-tree-node-char-rwidth (car n))
undo-tree-visualizer-spacing 1)))
(when (= (mod l 2) 1)
(setq n (cdr n))
(undo-tree-move-forward
(+ (undo-tree-node-char-lwidth (car n))
(/ undo-tree-visualizer-spacing 2) 1))))
(t (setq n (nthcdr (/ l 2) n))
(when (= (mod l 2) 1)
(undo-tree-move-backward
(+ (undo-tree-node-char-rwidth (car n))
(/ undo-tree-visualizer-spacing 2) 1))
(setq n (cdr n)))
(dotimes (i (- p (/ l 2) (mod l 2)))
(undo-tree-move-backward
(+ (undo-tree-node-char-lwidth (car n))
(undo-tree-node-char-rwidth (car n))
undo-tree-visualizer-spacing 1))
(setq n (cdr n)))
(undo-tree-move-backward
(+ (undo-tree-node-char-lwidth (car n))
(/ undo-tree-visualizer-spacing 2) 1)))))
(undo-tree-move-up 3)))
(defun undo-tree-timestamp-to-string
(timestamp &optional relative current register)
(if relative
(let ((time (floor (float-time
(subtract-time (current-time) timestamp))))
n)
(setq time
(if (> (setq n (/ time 315360000)) 0)
(if (> n 999) "-ages" (format "-%dy" n))
(setq time (% time 315360000))
(if (> (setq n (/ time 86400)) 0)
(format "-%dd" n)
(setq time (% time 86400))
(if (> (setq n (/ time 3600)) 0)
(format "-%dh" n)
(setq time (% time 3600))
(if (> (setq n (/ time 60)) 0)
(format "-%dm" n)
(format "-%ds" (% time 60)))))))
(setq time (concat
(if current "*" " ")
time
(if register (concat "[" (char-to-string register) "]")
" ")))
(setq n (length time))
(if (< n 9)
(concat (make-string (- 9 n) ? ) time)
time))
(concat (if current " *" " ")
(format-time-string "%H:%M:%S" timestamp)
(if register
(concat "[" (char-to-string register) "]")
" "))))
(define-derived-mode
undo-tree-visualizer-mode special-mode "undo-tree-visualizer"
"Major mode used in undo-tree visualizer.
The undo-tree visualizer can only be invoked from a buffer in
which `undo-tree-mode' is enabled. The visualizer displays the
undo history tree graphically, and allows you to browse around
the undo history, undoing or redoing the corresponding changes in
the parent buffer.
Within the undo-tree visualizer, the following keys are available:
\\{undo-tree-visualizer-mode-map}"
:syntax-table nil
:abbrev-table nil
(setq truncate-lines t)
(setq cursor-type nil)
(setq undo-tree-visualizer-selected-node nil))
(defun undo-tree-visualize-undo (&optional arg)
"Undo changes. A numeric ARG serves as a repeat count."
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(let ((old (undo-tree-current buffer-undo-tree))
current)
(let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face)
(inhibit-read-only t))
(undo-tree-draw-node old))
(switch-to-buffer-other-window undo-tree-visualizer-parent-buffer)
(deactivate-mark)
(unwind-protect
(let ((undo-tree-inhibit-kill-visualizer t)) (undo-tree-undo-1 arg))
(setq current (undo-tree-current buffer-undo-tree))
(switch-to-buffer-other-window undo-tree-visualizer-buffer-name)
(when undo-tree-visualizer-lazy-drawing
(undo-tree-expand-up old current))
(let ((inhibit-read-only t)) (undo-tree-draw-node current 'current))
(when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff)))))
(defun undo-tree-visualize-redo (&optional arg)
"Redo changes. A numeric ARG serves as a repeat count."
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(let ((old (undo-tree-current buffer-undo-tree))
current)
(let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face)
(inhibit-read-only t))
(undo-tree-draw-node (undo-tree-current buffer-undo-tree)))
(switch-to-buffer-other-window undo-tree-visualizer-parent-buffer)
(deactivate-mark)
(unwind-protect
(let ((undo-tree-inhibit-kill-visualizer t)) (undo-tree-redo-1 arg))
(setq current (undo-tree-current buffer-undo-tree))
(switch-to-buffer-other-window undo-tree-visualizer-buffer-name)
(when undo-tree-visualizer-lazy-drawing
(undo-tree-expand-down old current))
(let ((inhibit-read-only t)) (undo-tree-draw-node current 'current))
(when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff)))))
(defun undo-tree-visualize-switch-branch-right (arg)
"Switch to next branch of the undo tree.
This will affect which branch to descend when *redoing* changes
using `undo-tree-redo' or `undo-tree-visualizer-redo'."
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(goto-char (undo-tree-node-marker (undo-tree-current buffer-undo-tree)))
(let ((undo-tree-insert-face 'undo-tree-visualizer-default-face)
(inhibit-read-only t))
(undo-tree-highlight-active-branch (undo-tree-current buffer-undo-tree)))
(let ((branch (undo-tree-node-branch (undo-tree-current buffer-undo-tree))))
(setf (undo-tree-node-branch (undo-tree-current buffer-undo-tree))
(cond
((>= (+ branch arg) (undo-tree-num-branches))
(1- (undo-tree-num-branches)))
((<= (+ branch arg) 0) 0)
(t (+ branch arg))))
(let ((inhibit-read-only t))
(goto-char (undo-tree-node-marker (undo-tree-current buffer-undo-tree)))
(let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face))
(undo-tree-highlight-active-branch (undo-tree-current buffer-undo-tree)))
(undo-tree-draw-node (undo-tree-current buffer-undo-tree) 'current))))
(defun undo-tree-visualize-switch-branch-left (arg)
"Switch to previous branch of the undo tree.
This will affect which branch to descend when *redoing* changes
using `undo-tree-redo' or `undo-tree-visualizer-redo'."
(interactive "p")
(undo-tree-visualize-switch-branch-right (- arg)))
(defun undo-tree-visualizer-quit ()
"Quit the undo-tree visualizer."
(interactive)
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(undo-tree-clear-visualizer-data buffer-undo-tree)
(unwind-protect
(with-current-buffer undo-tree-visualizer-parent-buffer
(remove-hook 'before-change-functions 'undo-tree-kill-visualizer t))
(when undo-tree-visualizer-diff (undo-tree-visualizer-hide-diff))
(let ((parent undo-tree-visualizer-parent-buffer)
window)
(kill-buffer nil)
(unwind-protect
(if (setq window (get-buffer-window parent))
(select-window window)
(switch-to-buffer parent))))))
(defun undo-tree-visualizer-abort ()
"Quit the undo-tree visualizer and return buffer to original state."
(interactive)
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(let ((node undo-tree-visualizer-initial-node))
(undo-tree-visualizer-quit)
(undo-tree-set node)))
(defun undo-tree-visualizer-set (&optional pos)
"Set buffer to state corresponding to undo tree node
at POS, or point if POS is nil."
(interactive)
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(unless pos (setq pos (point)))
(let ((node (get-text-property pos 'undo-tree-node)))
(when node
(switch-to-buffer-other-window undo-tree-visualizer-parent-buffer)
(let ((undo-tree-inhibit-kill-visualizer t)) (undo-tree-set node))
(switch-to-buffer-other-window undo-tree-visualizer-buffer-name)
(let ((inhibit-read-only t)) (undo-tree-draw-tree buffer-undo-tree))
(when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff)))))
(defun undo-tree-visualizer-mouse-set (pos)
"Set buffer to state corresponding to undo tree node
at mouse event POS."
(interactive "@e")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(undo-tree-visualizer-set (event-start (nth 1 pos))))
(defun undo-tree-visualize-undo-to-x (&optional x)
"Undo to last branch point, register, or saved state.
If X is the symbol `branch', undo to last branch point. If X is
the symbol `register', undo to last register. If X is the sumbol
`saved', undo to last saved state. If X is null, undo to first of
these that's encountered.
Interactively, a single \\[universal-argument] specifies
`branch', a double \\[universal-argument] \\[universal-argument]
specifies `saved', and a negative prefix argument specifies
`register'."
(interactive "P")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(when (and (called-interactively-p 'any) x)
(setq x (prefix-numeric-value x)
x (cond
((< x 0) 'register)
((<= x 4) 'branch)
(t 'saved))))
(let ((current (if undo-tree-visualizer-selection-mode
undo-tree-visualizer-selected-node
(undo-tree-current buffer-undo-tree)))
(diff undo-tree-visualizer-diff)
r)
(undo-tree-visualizer-hide-diff)
(while (and (undo-tree-node-previous current)
(or (if undo-tree-visualizer-selection-mode
(progn
(undo-tree-visualizer-select-previous)
(setq current undo-tree-visualizer-selected-node))
(undo-tree-visualize-undo)
(setq current (undo-tree-current buffer-undo-tree)))
t)
(not (or (and (or (null x) (eq x 'branch))
(> (undo-tree-num-branches) 1))
(and (or (null x) (eq x 'register))
(setq r (undo-tree-node-register current))
(undo-tree-register-data-p
(setq r (registerv-data (get-register r))))
(eq current (undo-tree-register-data-node r)))
(and (or (null x) (eq x 'saved))
(undo-tree-node-unmodified-p current))
))))
(when diff
(undo-tree-visualizer-show-diff
(when undo-tree-visualizer-selection-mode
undo-tree-visualizer-selected-node)))))
(defun undo-tree-visualize-redo-to-x (&optional x)
"Redo to last branch point, register, or saved state.
If X is the symbol `branch', redo to last branch point. If X is
the symbol `register', redo to last register. If X is the sumbol
`saved', redo to last saved state. If X is null, redo to first of
these that's encountered.
Interactively, a single \\[universal-argument] specifies
`branch', a double \\[universal-argument] \\[universal-argument]
specifies `saved', and a negative prefix argument specifies
`register'."
(interactive "P")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(when (and (called-interactively-p 'any) x)
(setq x (prefix-numeric-value x)
x (cond
((< x 0) 'register)
((<= x 4) 'branch)
(t 'saved))))
(let ((current (if undo-tree-visualizer-selection-mode
undo-tree-visualizer-selected-node
(undo-tree-current buffer-undo-tree)))
(diff undo-tree-visualizer-diff)
r)
(undo-tree-visualizer-hide-diff)
(while (and (undo-tree-node-next current)
(or (if undo-tree-visualizer-selection-mode
(progn
(undo-tree-visualizer-select-next)
(setq current undo-tree-visualizer-selected-node))
(undo-tree-visualize-redo)
(setq current (undo-tree-current buffer-undo-tree)))
t)
(not (or (and (or (null x) (eq x 'branch))
(> (undo-tree-num-branches) 1))
(and (or (null x) (eq x 'register))
(setq r (undo-tree-node-register current))
(undo-tree-register-data-p
(setq r (registerv-data (get-register r))))
(eq current (undo-tree-register-data-node r)))
(and (or (null x) (eq x 'saved))
(undo-tree-node-unmodified-p current))
))))
(when diff
(undo-tree-visualizer-show-diff
(when undo-tree-visualizer-selection-mode
undo-tree-visualizer-selected-node)))))
(defun undo-tree-visualizer-toggle-timestamps ()
"Toggle display of time-stamps."
(interactive)
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(setq undo-tree-visualizer-timestamps (not undo-tree-visualizer-timestamps))
(setq undo-tree-visualizer-spacing (undo-tree-visualizer-calculate-spacing))
(let ((inhibit-read-only t)) (undo-tree-draw-tree buffer-undo-tree)))
(defun undo-tree-visualizer-scroll-left (&optional arg)
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(scroll-left (or arg 1) t))
(defun undo-tree-visualizer-scroll-right (&optional arg)
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(scroll-right (or arg 1) t))
(defun undo-tree-visualizer-scroll-up (&optional arg)
(interactive "P")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(if (or (and (numberp arg) (< arg 0)) (eq arg '-))
(undo-tree-visualizer-scroll-down arg)
(unwind-protect
(scroll-up-command arg)
(undo-tree-expand-down
(nth (undo-tree-node-branch (undo-tree-current buffer-undo-tree))
(undo-tree-node-next (undo-tree-current buffer-undo-tree)))))
(when (and (not undo-tree-visualizer-needs-extending-down) (eobp))
(scroll-up))))
(defun undo-tree-visualizer-scroll-down (&optional arg)
(interactive "P")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(if (or (and (numberp arg) (< arg 0)) (eq arg '-))
(undo-tree-visualizer-scroll-up arg)
(let ((scroll-lines
(or arg (- (window-height) next-screen-context-lines)))
(window-line (1- (line-number-at-pos (window-start)))))
(when (and undo-tree-visualizer-needs-extending-up
(< window-line scroll-lines))
(let ((inhibit-read-only t))
(goto-char (point-min))
(undo-tree-move-up (- scroll-lines window-line)))))
(unwind-protect
(scroll-down-command arg)
(undo-tree-expand-up
(undo-tree-node-previous (undo-tree-current buffer-undo-tree))))
(when (and (not undo-tree-visualizer-needs-extending-down) (bobp))
(scroll-down))))
(define-minor-mode undo-tree-visualizer-selection-mode
"Toggle mode to select nodes in undo-tree visualizer."
:lighter "Select"
:keymap undo-tree-visualizer-selection-mode-map
:group undo-tree
(cond
(undo-tree-visualizer-selection-mode
(setq cursor-type 'box)
(setq undo-tree-visualizer-selected-node
(undo-tree-current buffer-undo-tree))
(when undo-tree-visualizer-diff
(let ((buff (get-buffer undo-tree-diff-buffer-name))
(inhibit-read-only t))
(when buff (with-current-buffer buff (erase-buffer))))))
(t (setq cursor-type nil)
(setq undo-tree-visualizer-selected-node nil)
(goto-char (undo-tree-node-marker (undo-tree-current buffer-undo-tree)))
(when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff)))
))
(defun undo-tree-visualizer-select-previous (&optional arg)
"Move to previous node."
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(let ((node undo-tree-visualizer-selected-node))
(catch 'top
(dotimes (i (or arg 1))
(unless (undo-tree-node-previous node) (throw 'top t))
(setq node (undo-tree-node-previous node))))
(when undo-tree-visualizer-lazy-drawing
(undo-tree-expand-up undo-tree-visualizer-selected-node node))
(when (and undo-tree-visualizer-diff
(not (eq node undo-tree-visualizer-selected-node)))
(undo-tree-visualizer-update-diff node))
(goto-char (undo-tree-node-marker node))
(setq undo-tree-visualizer-selected-node node)))
(defun undo-tree-visualizer-select-next (&optional arg)
"Move to next node."
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(let ((node undo-tree-visualizer-selected-node))
(catch 'bottom
(dotimes (i (or arg 1))
(unless (nth (undo-tree-node-branch node) (undo-tree-node-next node))
(throw 'bottom t))
(setq node
(nth (undo-tree-node-branch node) (undo-tree-node-next node)))))
(when undo-tree-visualizer-lazy-drawing
(undo-tree-expand-down undo-tree-visualizer-selected-node node))
(when (and undo-tree-visualizer-diff
(not (eq node undo-tree-visualizer-selected-node)))
(undo-tree-visualizer-update-diff node))
(goto-char (undo-tree-node-marker node))
(setq undo-tree-visualizer-selected-node node)))
(defun undo-tree-visualizer-select-right (&optional arg)
"Move right to a sibling node."
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(let ((node undo-tree-visualizer-selected-node)
end)
(goto-char (undo-tree-node-marker undo-tree-visualizer-selected-node))
(setq end (line-end-position))
(catch 'end
(dotimes (i arg)
(while (or (null node) (eq node undo-tree-visualizer-selected-node))
(forward-char)
(setq node (get-text-property (point) 'undo-tree-node))
(when (= (point) end) (throw 'end t)))))
(goto-char (undo-tree-node-marker
(or node undo-tree-visualizer-selected-node)))
(when (and undo-tree-visualizer-diff node
(not (eq node undo-tree-visualizer-selected-node)))
(undo-tree-visualizer-update-diff node))
(when node (setq undo-tree-visualizer-selected-node node))))
(defun undo-tree-visualizer-select-left (&optional arg)
"Move left to a sibling node."
(interactive "p")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(let ((node (get-text-property (point) 'undo-tree-node))
beg)
(goto-char (undo-tree-node-marker undo-tree-visualizer-selected-node))
(setq beg (line-beginning-position))
(catch 'beg
(dotimes (i arg)
(while (or (null node) (eq node undo-tree-visualizer-selected-node))
(backward-char)
(setq node (get-text-property (point) 'undo-tree-node))
(when (= (point) beg) (throw 'beg t)))))
(goto-char (undo-tree-node-marker
(or node undo-tree-visualizer-selected-node)))
(when (and undo-tree-visualizer-diff node
(not (eq node undo-tree-visualizer-selected-node)))
(undo-tree-visualizer-update-diff node))
(when node (setq undo-tree-visualizer-selected-node node))))
(defun undo-tree-visualizer-select (pos)
(let ((node (get-text-property pos 'undo-tree-node)))
(when node
(goto-char (undo-tree-node-marker node))
(when undo-tree-visualizer-lazy-drawing
(undo-tree-expand-up undo-tree-visualizer-selected-node node)
(undo-tree-expand-down undo-tree-visualizer-selected-node node))
(when (and undo-tree-visualizer-diff
(not (eq node undo-tree-visualizer-selected-node)))
(undo-tree-visualizer-update-diff node))
(setq undo-tree-visualizer-selected-node node)
)))
(defun undo-tree-visualizer-mouse-select (pos)
"Select undo tree node at mouse event POS."
(interactive "@e")
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(undo-tree-visualizer-select (event-start (nth 1 pos))))
(defun undo-tree-visualizer-toggle-diff ()
"Toggle diff display in undo-tree visualizer."
(interactive)
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(if undo-tree-visualizer-diff
(undo-tree-visualizer-hide-diff)
(undo-tree-visualizer-show-diff)))
(defun undo-tree-visualizer-selection-toggle-diff ()
"Toggle diff display in undo-tree visualizer selection mode."
(interactive)
(unless (eq major-mode 'undo-tree-visualizer-mode)
(user-error "Undo-tree mode not enabled in buffer"))
(if undo-tree-visualizer-diff
(undo-tree-visualizer-hide-diff)
(let ((node (get-text-property (point) 'undo-tree-node)))
(when node (undo-tree-visualizer-show-diff node)))))
(defun undo-tree-visualizer-show-diff (&optional node)
(setq undo-tree-visualizer-diff t)
(let ((buff (with-current-buffer undo-tree-visualizer-parent-buffer
(undo-tree-diff node)))
(display-buffer-mark-dedicated 'soft)
win)
(setq win (split-window))
(set-window-buffer win buff)
(shrink-window-if-larger-than-buffer win)))
(defun undo-tree-visualizer-hide-diff ()
(setq undo-tree-visualizer-diff nil)
(let ((win (get-buffer-window undo-tree-diff-buffer-name)))
(when win (with-selected-window win (kill-buffer-and-window)))))
(defun undo-tree-diff (&optional node)
(let (tmpfile buff)
(let ((undo-tree-inhibit-kill-visualizer t)
(current (undo-tree-current buffer-undo-tree)))
(undo-tree-set (or node (undo-tree-node-previous current) current)
'preserve-timestamps)
(setq tmpfile (diff-file-local-copy (current-buffer)))
(undo-tree-set current 'preserve-timestamps))
(setq buff (diff-no-select
tmpfile (current-buffer) nil 'noasync
(get-buffer-create undo-tree-diff-buffer-name)))
(let ((inhibit-read-only t))
(with-current-buffer buff
(goto-char (point-min))
(delete-region (point) (1+ (line-end-position 3)))
(goto-char (point-max))
(forward-line -2)
(delete-region (point) (point-max))
(setq cursor-type nil)
(setq buffer-read-only t)))
buff))
(defun undo-tree-visualizer-update-diff (&optional node)
(with-current-buffer undo-tree-visualizer-parent-buffer
(undo-tree-diff node))
(let ((win (get-buffer-window undo-tree-diff-buffer-name)))
(when win
(balance-windows)
(shrink-window-if-larger-than-buffer win))))
(provide 'undo-tree)