;;; undo-tree.el --- Treat undo history as a tree -*- lexical-binding: t; -*-
-;; Copyright (C) 2009-2012 Free Software Foundation, Inc
+;; Copyright (C) 2009-2013 Free Software Foundation, Inc
;; Author: Toby Cubitt <toby-undo-tree@dr-qubit.org>
-;; Version: 0.6.3
+;; Version: 0.6.5
;; Keywords: convenience, files, undo, redo, history, tree
;; URL: http://www.dr-qubit.org/emacs.php
;; Repository: http://www.dr-qubit.org/git/undo-tree.git
;;
;; Persistent undo history:
;;
-;; Note: Requires a recent development version of Emacs checked out out from
-;; the Emacs bzr repository. All stable versions of Emacs currently
-;; break this feature.
+;; Note: Requires Emacs version 24.3 or higher.
;;
;; `undo-tree-auto-save-history' (variable)
;; automatically save and restore undo-tree history along with buffer
;;
;; (defadvice undo-tree-make-history-save-file-name
;; (after undo-tree activate)
-;; (setq concat ad-return-value ".gz"))
+;; (setq ad-return-value (concat ad-return-value ".gz")))
;;
;;
;;
(file-local-copy file-or-buf))))
+;; `user-error' isn't defined in Emacs < 24.3
+(unless (fboundp 'user-error)
+ (defalias 'user-error 'error)
+ ;; prevent debugger being called on user errors
+ (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"))
+
+
+
\f
;;; =====================================================================
(defvar buffer-undo-tree nil
"Tree of undo entries in current buffer.")
-(make-variable-buffer-local 'buffer-undo-tree)
(put 'buffer-undo-tree 'permanent-local t)
+(make-variable-buffer-local 'buffer-undo-tree)
(defgroup undo-tree nil
It will automatically load undo history when a buffer is loaded
from file, if an undo save file exists.
-Undo-tree history is saved to a file called
-\".<buffer-file-name>.~undo-tree\" in the same directory as the
-file itself.
+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
"When non-nil, display time-stamps by default
in undo-tree visualizer.
-\\<undo-tree-visualizer-map>You can always toggle time-stamps on and off \
+\\<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
(defcustom undo-tree-visualizer-diff nil
"When non-nil, display diff by default in undo-tree visualizer.
-\\<undo-tree-visualizer-map>You can always toggle the diff display \
+\\<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
value.
Lazy drawing means that only the visible portion of the tree will
-be drawn initially , and the tree will be extended later as
+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.
(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)
;; stores modification time of parent buffer's file, if any
(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)
;; stores current horizontal spacing needed for drawing undo-tree
(defvar undo-tree-visualizer-spacing nil)
+(put 'undo-tree-visualizer-spacing 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-spacing)
;; calculate horizontal spacing required for drawing tree with current
;; holds node that was current when visualizer was invoked
(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)
;; holds currently selected node in visualizer selection mode
(defvar undo-tree-visualizer-selected-node nil)
+(put 'undo-tree-visualizer-selected-node 'permanent-local t)
(make-variable-buffer-local 'undo-tree-visualizer-selected)
;; used to store nodes at edge of currently drawn portion of tree
(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)
;; dynamically bound to t when undoing from visualizer, to inhibit
(defconst undo-tree-visualizer-buffer-name " *undo-tree*")
(defconst undo-tree-diff-buffer-name "*undo-tree Diff*")
-;; prevent debugger being called on "No further redo information"
-(add-to-list 'debug-ignored-errors "^No further redo information")
-(add-to-list 'debug-ignored-errors "^No further redo information for region")
-
;; install history-auto-save hooks
(add-hook 'write-file-functions 'undo-tree-save-history-hook)
(add-hook 'find-file-hook 'undo-tree-load-history-hook)
(setq undo-tree-map map)))
-(defvar undo-tree-visualizer-map nil
+(defvar undo-tree-visualizer-mode-map nil
"Keymap used in undo-tree visualizer.")
-(unless undo-tree-visualizer-map
+(unless undo-tree-visualizer-mode-map
(let ((map (make-sparse-keymap)))
;; vertical motion keys undo/redo
(define-key map [remap previous-line] 'undo-tree-visualize-undo)
(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-down] 'undo-tree-visualize-undo-to-x)
- (define-key map [C-up] '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)
;; mouse sets buffer state to node at click
(define-key map [mouse-1] 'undo-tree-visualizer-mouse-set)
;; toggle timestamps
(define-key map "t" 'undo-tree-visualizer-toggle-timestamps)
;; toggle diff
(define-key map "d" 'undo-tree-visualizer-toggle-diff)
- ;; selection mode
+ ;; toggle selection mode
(define-key map "s" 'undo-tree-visualizer-selection-mode)
;; horizontal scrolling may be needed if the tree is very wide
(define-key map "," 'undo-tree-visualizer-scroll-left)
(define-key map "q" 'undo-tree-visualizer-quit)
(define-key map "\C-q" 'undo-tree-visualizer-abort)
;; set keymap
- (setq undo-tree-visualizer-map map)))
+ (setq undo-tree-visualizer-mode-map map)))
-(defvar undo-tree-visualizer-selection-map nil
+(defvar undo-tree-visualizer-selection-mode-map nil
"Keymap used in undo-tree visualizer selection mode.")
-(unless undo-tree-visualizer-selection-map
+(unless undo-tree-visualizer-selection-mode-map
(let ((map (make-sparse-keymap)))
;; vertical motion keys move up and down tree
(define-key map [remap previous-line]
(lambda () (interactive) (undo-tree-visualizer-select-left 10)))
(define-key map ">"
(lambda () (interactive) (undo-tree-visualizer-select-right 10)))
- ;; mouse or <enter> sets buffer state to node at point/click
+ ;; <enter> sets buffer state to node at point
(define-key map "\r" 'undo-tree-visualizer-set)
- (define-key map [mouse-1] 'undo-tree-visualizer-mouse-set)
- ;; toggle timestamps
- (define-key map "t" 'undo-tree-visualizer-toggle-timestamps)
+ ;; mouse selects node at click
+ (define-key map [mouse-1] 'undo-tree-visualizer-mouse-select)
;; toggle diff
(define-key map "d" 'undo-tree-visualizer-selection-toggle-diff)
- ;; quit visualizer selection mode
- (define-key map "s" 'undo-tree-visualizer-mode)
- ;; quit visualizer
- (define-key map "q" 'undo-tree-visualizer-quit)
- (define-key map "\C-q" 'undo-tree-visualizer-abort)
;; set keymap
- (setq undo-tree-visualizer-selection-map map)))
+ (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
+ ;; save old undo menu item, and install undo/redo menu items
+ (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))
+ (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))
+ (undo-tree-node-next
+ (undo-tree-current buffer-undo-tree)))
+ :help "Redo last operation")
+ 'undo))
+ ;; uninstall undo/redo menu items
+ (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)
+
Within the undo-tree visualizer, the following keys are available:
- \\{undo-tree-visualizer-map}"
+ \\{undo-tree-visualizer-mode-map}"
nil ; init value
undo-tree-mode-lighter ; lighter
;; if disabling `undo-tree-mode', rebuild `buffer-undo-list' from tree so
;; Emacs undo can work
- (if (not undo-tree-mode)
+ (when (not undo-tree-mode)
(undo-list-rebuild-from-tree)
(setq buffer-undo-tree nil)))
changes within the current region."
(interactive "*P")
;; throw error if undo is disabled in buffer
- (when (eq buffer-undo-list t) (error "No undo information in this buffer"))
+ (when (eq buffer-undo-list t)
+ (user-error "No undo information in this buffer"))
(undo-tree-undo-1 arg)
;; inform user if at branch point
(when (> (undo-tree-num-branches) 1) (message "Undo branch point!")))
(dotimes (i (or (and (numberp arg) (prefix-numeric-value arg)) 1))
;; check if at top of undo tree
(unless (undo-tree-node-previous (undo-tree-current buffer-undo-tree))
- (error "No further undo information"))
+ (user-error "No further undo information"))
;; if region is active, or a non-numeric prefix argument was supplied,
;; try to pull out a new branch of changes affecting the region
(when (and undo-in-region
(not (undo-tree-pull-undo-in-region-branch
(region-beginning) (region-end))))
- (error "No further undo information for region"))
+ (user-error "No further undo information for region"))
;; remove any GC'd elements from node's undo list
(setq current (undo-tree-current buffer-undo-tree))
changes within the current region."
(interactive "*P")
;; throw error if undo is disabled in buffer
- (when (eq buffer-undo-list t) (error "No undo information in this buffer"))
+ (when (eq buffer-undo-list t)
+ (user-error "No undo information in this buffer"))
(undo-tree-redo-1 arg)
;; inform user if at branch point
(when (> (undo-tree-num-branches) 1) (message "Undo branch point!")))
(dotimes (i (or (and (numberp arg) (prefix-numeric-value arg)) 1))
;; check if at bottom of undo tree
(when (null (undo-tree-node-next (undo-tree-current buffer-undo-tree)))
- (error "No further redo information"))
+ (user-error "No further redo information"))
;; if region is active, or a non-numeric prefix argument was supplied,
;; try to pull out a new branch of changes affecting the region
(when (and redo-in-region
(not (undo-tree-pull-redo-in-region-branch
(region-beginning) (region-end))))
- (error "No further redo information for region"))
+ (user-error "No further redo information for region"))
;; get next node (but DON'T advance current node in tree yet, in case
;; redoing fails)
(1- (undo-tree-num-branches)) b)))
))))))
;; throw error if undo is disabled in buffer
- (when (eq buffer-undo-list t) (error "No undo information in this buffer"))
+ (when (eq buffer-undo-list t)
+ (user-error "No undo information in this buffer"))
;; sanity check branch number
- (when (<= (undo-tree-num-branches) 1) (error "Not at undo branch point"))
+ (when (<= (undo-tree-num-branches) 1)
+ (user-error "Not at undo branch point"))
(when (or (< branch 0) (> branch (1- (undo-tree-num-branches))))
- (error "Invalid branch number"))
+ (user-error "Invalid branch number"))
;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree'
(undo-list-transfer-to-tree)
;; switch branch
Argument is a character, naming the register."
(interactive "cUndo-tree state to register: ")
;; throw error if undo is disabled in buffer
- (when (eq buffer-undo-list t) (error "No undo information in this buffer"))
+ (when (eq buffer-undo-list t)
+ (user-error "No undo information in this buffer"))
;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree'
(undo-list-transfer-to-tree)
;; save current node to REGISTER
(let ((data (registerv-data (get-register register))))
(cond
((eq buffer-undo-list t)
- (error "No undo information in this buffer"))
+ (user-error "No undo information in this buffer"))
((not (undo-tree-register-data-p data))
- (error "Register doesn't contain undo-tree state"))
+ (user-error "Register doesn't contain undo-tree state"))
((not (eq (current-buffer) (undo-tree-register-data-buffer data)))
- (error "Register contains undo-tree state for a different buffer")))
+ (user-error "Register contains undo-tree state for a different buffer")))
;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree'
(undo-list-transfer-to-tree)
;; restore buffer state corresponding to saved node
(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.
+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. If the directory for the
-backup doesn't exist, it is created."
+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~")))
+ ".~undo-tree~")))
(defun undo-tree-save-history (&optional filename overwrite)
If OVERWRITE is non-nil, any existing file will be overwritten
without asking for confirmation."
(interactive)
- (when (eq buffer-undo-list t) (error "No undo information in this 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
(setq hash (read (current-buffer)))
(error
(kill-buffer nil)
- (funcall (if noerror 'message 'error)
+ (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 'error)
+ (funcall (if noerror 'message 'user-error)
"Buffer has been modified; could not load undo-tree history")
(throw 'load-error nil))
(condition-case nil
(defun undo-tree-load-history-hook ()
(when (and undo-tree-mode undo-tree-auto-save-history
- (not (eq buffer-undo-list t)))
+ (not (eq buffer-undo-list t))
+ (not revert-buffer-in-progress-p))
(undo-tree-load-history nil t)))
(interactive "*")
(deactivate-mark)
;; throw error if undo is disabled in buffer
- (when (eq buffer-undo-list t) (error "No undo information in this buffer"))
+ (when (eq buffer-undo-list t)
+ (user-error "No undo information in this buffer"))
;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree'
(undo-list-transfer-to-tree)
;; add hook to kill visualizer buffer if original buffer is changed
(setq undo-tree-visualizer-parent-mtime
(and (buffer-file-name buff)
(nth 5 (file-attributes (buffer-file-name buff)))))
- (setq buffer-undo-tree undo-tree)
(setq undo-tree-visualizer-initial-node (undo-tree-current undo-tree))
(setq undo-tree-visualizer-spacing
(undo-tree-visualizer-calculate-spacing))
(make-local-variable 'undo-tree-visualizer-timestamps)
(make-local-variable 'undo-tree-visualizer-diff)
+ (setq buffer-undo-tree undo-tree)
+ (undo-tree-visualizer-mode)
+ ;; FIXME; don't know why `undo-tree-visualizer-mode' clears this
+ (setq buffer-undo-tree undo-tree)
(set (make-local-variable 'undo-tree-visualizer-lazy-drawing)
(or (eq undo-tree-visualizer-lazy-drawing t)
(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))
- (undo-tree-visualizer-mode)
(let ((inhibit-read-only t)) (undo-tree-draw-tree undo-tree))))
(defun undo-tree-kill-visualizer (&rest _dummy)
;; Kill visualizer. Added to `before-change-functions' hook of original
;; buffer when visualizer is invoked.
- (unless undo-tree-inhibit-kill-visualizer
- (unwind-protect
- (with-current-buffer undo-tree-visualizer-buffer-name
- (undo-tree-visualizer-quit)))))
+ (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))))
(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)
;; lazy drawing starts vertically centred and displaced horizontally to
;; integer, extend up as far as that line. Otherwise, only extend visible
;; portion of tree. NODE is assumed to already have a node marker. Returns
;; non-nil if anything was actually extended.
- (let ((extended nil) parent n)
+ (let ((extended nil) parent)
;; don't bother extending if TOP specifies an already-drawn node
(unless (and (undo-tree-node-p top) (undo-tree-node-marker top))
(while node
(< top (line-number-at-pos
(undo-tree-node-marker node))))
(and (null top)
- ;; NOTE: check point in case window-start is outdated
+ ;; NOTE: we check point in case window-start is outdated
(< (min (line-number-at-pos (point))
(line-number-at-pos (window-start)))
(line-number-at-pos
(concat (make-string (- 9 n) ? ) time)
time))
;; absolute time
- (concat (if current "*" " ")
+ (concat (if current " *" " ")
(format-time-string "%H:%M:%S" timestamp)
(if register
(concat "[" (char-to-string register) "]")
;;; =====================================================================
;;; Visualizer commands
-(defun undo-tree-visualizer-mode ()
+(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
Within the undo-tree visualizer, the following keys are available:
- \\{undo-tree-visualizer-map}"
- (interactive)
- (setq major-mode 'undo-tree-visualizer-mode)
- (setq mode-name "undo-tree-visualizer-mode")
- (use-local-map undo-tree-visualizer-map)
+ \\{undo-tree-visualizer-mode-map}"
+ :syntax-table nil
+ :abbrev-table nil
(setq truncate-lines t)
(setq cursor-type nil)
- (setq buffer-read-only t)
- (setq undo-tree-visualizer-selected-node nil)
- (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff)))
+ (setq undo-tree-visualizer-selected-node nil))
(defun undo-tree-visualize-undo-to-x (&optional x)
"Undo to last branch point, register, or saved state.
-If X is 'branch, undo to last branch point. If X is 'register,
-undo to last register. If X is 'saved, undo to last 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]
-spcified `saved', and a negative prefix argument specifies
+`branch', a double \\[universal-argument] \\[universal-argument]
+specifies `saved', and a negative prefix argument specifies
`register'."
(interactive "P")
(when (and (called-interactively-p 'any) x)
((< x 0) 'register)
((<= x 4) 'branch)
(t 'saved))))
- (let ((current (undo-tree-current buffer-undo-tree))
+ (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 (undo-tree-visualize-undo) t)
- (setq current (undo-tree-current buffer-undo-tree))
+ (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)
;; branch point
(not (or (and (or (null x) (eq x 'branch))
(> (undo-tree-num-branches) 1))
;; saved state
(and (or (null x) (eq x 'saved))
(undo-tree-node-unmodified-p current))
- ))))))
+ ))))
+ ;; update diff display, if any
+ (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 next branch point or register.
-If X is the symbol `branch', redo to next branch point ignoring
-registers. If X is the symbol 'register', redo to next register,
-ignoring branch points.
+ "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 positive prefix argument specifies `branch', and
-a negative prefix argument specifies `register'."
+Interactively, a single \\[universal-argument] specifies
+`branch', a double \\[universal-argument] \\[universal-argument]
+specifies `saved', and a negative prefix argument specifies
+`register'."
(interactive "P")
(when (and (called-interactively-p 'any) x)
(setq x (prefix-numeric-value x)
((< x 0) 'register)
((<= x 4) 'branch)
(t 'saved))))
- (let ((current (undo-tree-current buffer-undo-tree))
+ (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 (undo-tree-visualize-redo) t)
- (setq current (undo-tree-current buffer-undo-tree))
+ (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)
;; branch point
(not (or (and (or (null x) (eq x 'branch))
(> (undo-tree-num-branches) 1))
;; saved state
(and (or (null x) (eq x 'saved))
(undo-tree-node-unmodified-p current))
- ))))))
+ ))))
+ ;; update diff display, if any
+ (when diff
+ (undo-tree-visualizer-show-diff
+ (when undo-tree-visualizer-selection-mode
+ undo-tree-visualizer-selected-node)))))
(defun undo-tree-visualizer-toggle-timestamps ()
;;; =====================================================================
;;; Visualizer selection mode
-(defun undo-tree-visualizer-selection-mode ()
- "Major mode used to select nodes in undo-tree visualizer."
- (interactive)
- (setq major-mode 'undo-tree-visualizer-selection-mode)
- (setq mode-name "undo-tree-visualizer-selection-mode")
- (use-local-map undo-tree-visualizer-selection-map)
- (setq cursor-type 'box)
- (setq undo-tree-visualizer-selected-node
- (undo-tree-current buffer-undo-tree))
- ;; erase diff (if any), as initially selected node is identical to current
- (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))))))
+(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
+ ;; enable selection mode
+ (undo-tree-visualizer-selection-mode
+ (setq cursor-type 'box)
+ (setq undo-tree-visualizer-selected-node
+ (undo-tree-current buffer-undo-tree))
+ ;; erase diff (if any), as initially selected node is identical to current
+ (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 ;; disable selection mode
+ (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)
(interactive "p")
(let ((node undo-tree-visualizer-selected-node))
(catch 'top
- (dotimes (i arg)
+ (dotimes (i (or arg 1))
(unless (undo-tree-node-previous node) (throw 'top t))
(setq node (undo-tree-node-previous node))))
;; when using lazy drawing, extend tree upwards as required
(interactive "p")
(let ((node undo-tree-visualizer-selected-node))
(catch 'bottom
- (dotimes (i arg)
+ (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 using lazy drawing, extend tree upwards as required
+ ;; when using lazy drawing, extend tree downwards as required
(when undo-tree-visualizer-lazy-drawing
(undo-tree-expand-down undo-tree-visualizer-selected-node node))
;; update diff display, if any
(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
+ ;; select node at POS
+ (goto-char (undo-tree-node-marker node))
+ ;; when using lazy drawing, extend tree up and down as required
+ (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))
+ ;; update diff display, if any
+ (when (and undo-tree-visualizer-diff
+ (not (eq node undo-tree-visualizer-selected-node)))
+ (undo-tree-visualizer-update-diff node))
+ ;; update selected 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")
+ (undo-tree-visualizer-select (event-start (nth 1 pos))))
+
+
+
\f
;;; =====================================================================
;;; Visualizer diff display
(defun undo-tree-diff (&optional node)
- ;; Create diff between current state and NODE (or previous state, if NODE is
- ;; null). Returns buffer containing diff.
+ ;; Create diff between NODE and current state (or previous state and current
+ ;; state, if NODE is null). Returns buffer containing diff.
(let (tmpfile buff)
;; generate diff
(let ((undo-tree-inhibit-kill-visualizer t)
(setq tmpfile (diff-file-local-copy (current-buffer)))
(undo-tree-set current 'preserve-timestamps))
(setq buff (diff-no-select
- (current-buffer) tmpfile nil 'noasync
+ tmpfile (current-buffer) nil 'noasync
(get-buffer-create undo-tree-diff-buffer-name)))
;; delete process messages and useless headers from diff buffer
- (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))
+ (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))