emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/undo-tree 5d06288 130/195: Implement lazy undo-tree dra


From: Stefan Monnier
Subject: [elpa] externals/undo-tree 5d06288 130/195: Implement lazy undo-tree drawing in visualizer.
Date: Sat, 28 Nov 2020 13:41:37 -0500 (EST)

branch: externals/undo-tree
commit 5d06288c4618ca47aebdc2164d8a03356972798d
Author: Toby S. Cubitt <toby-undo-tree@dr-qubit.org>
Commit: Toby S. Cubitt <toby-undo-tree@dr-qubit.org>

    Implement lazy undo-tree drawing in visualizer.
---
 undo-tree.el | 656 ++++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 489 insertions(+), 167 deletions(-)

diff --git a/undo-tree.el b/undo-tree.el
index de91a8a..e648bee 100644
--- a/undo-tree.el
+++ b/undo-tree.el
@@ -3,7 +3,7 @@
 ;; Copyright (C) 2009-2012  Free Software Foundation, Inc
 
 ;; Author: Toby Cubitt <toby-undo-tree@dr-qubit.org>
-;; Version: 0.5.6
+;; Version: 0.6
 ;; 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
@@ -51,8 +51,9 @@
 ;; Installation
 ;; ============
 ;;
-;; This package has only been tested with Emacs versions 22, 23 and CVS. It
-;; will not work without modifications in earlier versions of Emacs.
+;; This package has only been tested with Emacs versions 24 and CVS. It should
+;; work in Emacs versions 22 and 23 too, but will not work without
+;; modifications in earlier versions of Emacs.
 ;;
 ;; To install `undo-tree-mode', make sure this file is saved in a directory in
 ;; your `load-path', and add the line:
@@ -849,6 +850,24 @@ when `undo-tree-mode' is enabled."
   :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.
@@ -897,6 +916,7 @@ ignored."
                       (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.
@@ -915,7 +935,6 @@ using \\[undo-tree-visualizer-toggle-timestamps], 
regardless of the
 setting of this variable."
   :group 'undo-tree
   :type 'boolean)
-(make-variable-buffer-local 'undo-tree-visualizer-timestamps)
 
 
 (defcustom undo-tree-visualizer-diff nil
@@ -926,25 +945,31 @@ using \\[undo-tree-visualizer-toggle-diff], regardless of 
the
 setting of this variable."
   :group 'undo-tree
   :type 'boolean)
-(make-variable-buffer-local 'undo-tree-visualizer-diff)
 
 
-(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-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.
 
-(defcustom undo-tree-enable-undo-in-region t
-  "When non-nil, enable undo-in-region.
+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.
 
-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."
+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 'boolean)
+  :type '(choice (const :tag "never" nil)
+                (const :tag "always" t)
+                (integer :tag "> size")))
 
 
 (defface undo-tree-visualizer-default-face
@@ -1005,23 +1030,28 @@ in visualizer."
 (defvar undo-tree-visualizer-selected-node nil)
 (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)
+(make-variable-buffer-local 'undo-tree-visualizer-needs-extending-down)
+(defvar undo-tree-visualizer-needs-extending-up nil)
+(make-variable-buffer-local 'undo-tree-visualizer-needs-extending-up)
+
 ;; dynamically bound to t when undoing from visualizer, to inhibit
 ;; `undo-tree-kill-visualizer' hook function in parent buffer
 (defvar undo-tree-inhibit-kill-visualizer nil)
 
-;; can be let-bound to a face name, to set face used in drawing functions
+;; can be let-bound to a face name, used in drawing functions
 (defvar undo-tree-insert-face nil)
 
-
+;; visualizer buffer names
 (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" errors
+;; 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
+;; 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)
 
@@ -1029,7 +1059,7 @@ in visualizer."
 
 
 ;;; =================================================================
-;;;                     Setup default keymaps
+;;;                          Default keymaps
 
 (defvar undo-tree-map nil
   "Keymap used in undo-tree-mode.")
@@ -1096,14 +1126,14 @@ in visualizer."
     (define-key map "d" 'undo-tree-visualizer-toggle-diff)
     ;; selection mode
     (define-key map "s" 'undo-tree-visualizer-selection-mode)
-    ;; horizontal scrolling may be needed if the tree is very wide
+    ;; horizontal scrolling may be needed if the tree wide
     (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)
-    ;; vertical scrolling may be needed if the tree is very tall
-    (define-key map [next] 'scroll-up)
-    (define-key map [prior] 'scroll-down)
+    ;; vertical scrolling may be needed if the tree tall
+    (define-key map [next] 'undo-tree-visualizer-scroll-up)
+    (define-key map [prior] 'undo-tree-visualizer-scroll-down)
     ;; quit/abort visualizer
     (define-key map "q" 'undo-tree-visualizer-quit)
     (define-key map "\C-q" 'undo-tree-visualizer-abort)
@@ -1180,10 +1210,11 @@ in visualizer."
                   (root (undo-tree-make-node nil nil))
                   (current root)
                   (size 0)
+                 (count 0)
                  (object-pool (make-hash-table :test 'eq :weakness 'value))))
    ;;(:copier nil)
    )
-  root current size object-pool)
+  root current size count object-pool)
 
 
 
@@ -1529,7 +1560,7 @@ Comparison is done with `eq'."
 
 
 ;;; =====================================================================
-;;;    Utility functions for handling `buffer-undo-list' and changesets
+;;;             Undo list and undo changeset utility functions
 
 (defmacro undo-list-marker-elt-p (elt)
   `(markerp (car-safe ,elt)))
@@ -1655,14 +1686,16 @@ Comparison is done with `eq'."
     ;; `buffer-undo-tree' current node, and make new node the current node
     (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))))
+          (size (undo-list-byte-size (undo-tree-node-undo node)))
+          (count 1))
       (setf (undo-tree-current buffer-undo-tree) node)
       ;; grow tree fragment backwards using `buffer-undo-list' changesets
       (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 size (undo-list-byte-size (undo-tree-node-undo node)))
+       (incf count))
       ;; if no undo history has been discarded from `buffer-undo-list' since
       ;; last transfer, splice new tree fragment onto end of old
       ;; `buffer-undo-tree' current node
@@ -1672,13 +1705,15 @@ Comparison is done with `eq'."
            (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-size buffer-undo-tree) size)
+           (incf (undo-tree-count buffer-undo-tree) count))
        ;; if undo history has been discarded, replace entire
        ;; `buffer-undo-tree' with new tree fragment
        (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))))
     ;; discard undo history if necessary
     (undo-tree-discard-history)))
@@ -1736,7 +1771,7 @@ Comparison is done with `eq'."
 
 
 ;;; =====================================================================
-;;;                  History discarding functions
+;;;                History discarding utility functions
 
 (defun undo-tree-oldest-leaf (node)
   ;; Return oldest leaf node below NODE.
@@ -1780,6 +1815,7 @@ Comparison is done with `eq'."
          (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))
          ;; discard new root's undo data and PREVIOUS link
          (setf (undo-tree-node-undo node) nil
                (undo-tree-node-redo node) nil
@@ -1804,12 +1840,14 @@ Comparison is done with `eq'."
        (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))
+       ;; discard leaf
         (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 parent has branches, or parent is current node, next node to
-        ;; discard is oldest leaf, otherwise it's parent
+        ;; discard is oldest leaf, otherwise it's the parent itself
         (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)))
@@ -1903,7 +1941,7 @@ which is defined in the `warnings' library.\n")
 
 
 ;;; =====================================================================
-;;;                 Visualizer-related functions
+;;;                   Visualizer utility functions
 
 (defun undo-tree-compute-widths (node)
   "Recursively compute widths for nodes below NODE."
@@ -1992,11 +2030,11 @@ which is defined in the `warnings' library.\n")
       (vector lwidth cwidth rwidth))))
 
 
-(defun undo-tree-clear-visualizer-data (node)
+(defun undo-tree-clear-visualizer-data (tree)
   ;; Clear visualizer data below NODE.
   (undo-tree-mapc
    (lambda (n) (undo-tree-node-clear-visualizer-data n))
-   node))
+   (undo-tree-root tree)))
 
 
 (defun undo-tree-node-saved-buffer-p (node &optional mtime)
@@ -2023,7 +2061,7 @@ which is defined in the `warnings' library.\n")
 
 
 ;;; =====================================================================
-;;;                     Undo-in-region functions
+;;;                  Undo-in-region utility functions
 
 ;; `undo-elt-in-region' uses this as a dynamically-scoped variable
 (defvar undo-adjusted-markers nil)
@@ -2254,12 +2292,10 @@ which is defined in the `warnings' library.\n")
        (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)))
-                     (when (undo-tree-node-redo node)
-                       (incf (undo-tree-size buffer-undo-tree)
-                             (undo-list-byte-size (undo-tree-node-redo node))))
-                     )))
+                           (+ (undo-list-byte-size (undo-tree-node-undo node))
+                              (undo-list-byte-size (undo-tree-node-redo 
node)))))))
        t)  ; indicate undo-in-region branch was successfully pulled
       )))
 
@@ -2415,14 +2451,14 @@ which is defined in the `warnings' library.\n")
        ;; update undo-tree size
        (unless repeated-redo-in-region
          (setq node fragment)
-         (while (progn
-                  (and (setq node (car (undo-tree-node-next node)))
-                       (incf (undo-tree-size buffer-undo-tree)
-                             (undo-list-byte-size
-                              (undo-tree-node-redo node)))))))
+         (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)  ; indicate undo-in-region branch was successfully pulled
+       t)  ; indicate redo-in-region branch was successfully pulled
       )))
 
 
@@ -2680,9 +2716,8 @@ changes within the current region."
        ;; otherwise, record redo entries that `primitive-undo' has added to
        ;; `buffer-undo-list' in current node's redo record, replacing
        ;; existing entry if one already exists
-       (when (undo-tree-node-redo current)
-         (decf (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)
@@ -2791,9 +2826,8 @@ changes within the current region."
        ;; otherwise, record undo entries that `primitive-undo' has added to
        ;; `buffer-undo-list' in current node's undo record, replacing
        ;; existing entry if one already exists
-       (when (undo-tree-node-undo current)
-         (decf (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)
@@ -2931,7 +2965,7 @@ Argument is a character, naming the register."
 
 
 ;;; =====================================================================
-;;;                       Persistent storage
+;;;                    Persistent storage commands
 
 (defun undo-tree-make-history-save-file-name (file)
   "Create the undo history file name for FILE.
@@ -2962,8 +2996,7 @@ without asking for confirmation."
   (when (and buffer-undo-tree (not (eq buffer-undo-tree t)))
     (condition-case nil
        (undo-tree-kill-visualizer)
-      (error (undo-tree-clear-visualizer-data
-             (undo-tree-root buffer-undo-tree))))
+      (error (undo-tree-clear-visualizer-data buffer-undo-tree)))
     (let ((buff (current-buffer))
          tree)
       ;; get filename
@@ -3075,7 +3108,7 @@ signaling an error if file is not found."
 
 
 ;;; =====================================================================
-;;;                       Undo-tree visualizer
+;;;                    Visualizer drawing functions
 
 (defun undo-tree-visualize ()
   "Visualize the current buffer's undo tree."
@@ -3101,11 +3134,16 @@ signaling an error if file is not found."
     (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)
+    (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-root undo-tree)
-                          (undo-tree-current undo-tree)))))
+    (let ((inhibit-read-only t)) (undo-tree-draw-tree undo-tree))))
 
 
 (defun undo-tree-kill-visualizer (&rest _dummy)
@@ -3118,64 +3156,223 @@ signaling an error if file is not found."
 
 
 
-(defun undo-tree-draw-tree (node current)
-  ;; Draw undo-tree in current buffer starting from NODE. CURRENT specifies
-  ;; the current node in the tree.
-  (let (seen-current)
+(defun undo-tree-draw-tree (undo-tree)
+  ;; Draw undo-tree in current buffer starting from NODE (or root if nil).
+  (let ((node (if undo-tree-visualizer-lazy-drawing
+                 (undo-tree-current undo-tree)
+               (undo-tree-root undo-tree))))
     (erase-buffer)
-    (undo-tree-move-down 1)      ; top margin
-    (undo-tree-clear-visualizer-data node)
+    (undo-tree-clear-visualizer-data undo-tree)
     (undo-tree-compute-widths node)
-    (undo-tree-move-forward
-     (max (/ (window-width) 2)
-         (+ (undo-tree-node-char-lwidth node)
-            ;; add space for left part of left-most time-stamp
-            (if undo-tree-visualizer-timestamps
-                (/ (- undo-tree-visualizer-spacing 4) 2)
-              0)
-            2)))  ; left margin
+    ;; lazy drawing starts vertically centred and displaced horizontally to
+    ;; the left (window-width/4), since trees will typically grow right
+    (if undo-tree-visualizer-lazy-drawing
+       (progn
+         (undo-tree-move-down (/ (window-height) 2))
+         (undo-tree-move-forward (max 2 (/ (window-width) 4)))) ; left margin
+      ;; non-lazy drawing starts in centre at top of buffer
+      (undo-tree-move-down 1)  ; top margin
+      (undo-tree-move-forward
+       (max (/ (window-width) 2)
+           (+ (undo-tree-node-char-lwidth node)
+              ;; add space for left part of left-most time-stamp
+              (if undo-tree-visualizer-timestamps
+                  (/ (- undo-tree-visualizer-spacing 4) 2)
+                0)
+              2))))  ; left margin
+    ;; link starting node to its representation in visualizer
+    (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))
     ;; draw undo-tree
     (let ((undo-tree-insert-face 'undo-tree-visualizer-default-face)
-         (cur-stack (list node))
-         next-stack
-         (n node))
-      ;; flag to indicate if current node was drawn
-      (setq seen-current (eq node current))
-      ;; link starting node to its representation in visualizer
-      (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))
-      ;; draw nodes from current layer
-      (while (or cur-stack (prog1 (setq cur-stack next-stack)
-                            (setq next-stack nil)))
-       (setq n (pop cur-stack)
-             seen-current (or seen-current (eq n current)))
-       (goto-char (undo-tree-node-marker n))
-       (setq n (undo-tree-draw-subtree n nil)
-             next-stack (nconc n next-stack))))
+         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)))))
     ;; highlight active branch
-    (goto-char (undo-tree-node-marker node))
     (let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face))
-      (undo-tree-highlight-active-branch node))
-    ;; highlight current node if drawn
-    (when seen-current (undo-tree-draw-node current 'current))))
-
-
-(defun undo-tree-highlight-active-branch (node)
-  ;; Draw highlighted active branch below NODE in current buffer.
+      (undo-tree-highlight-active-branch
+       (or undo-tree-visualizer-needs-extending-up
+          (undo-tree-root undo-tree))))
+    ;; highlight current node
+    (undo-tree-draw-node (undo-tree-current undo-tree) 'current)))
+
+
+(defun undo-tree-extend-down (node &optional bottom)
+  ;; Extend tree downwards starting from NODE and point. If BOTTOM is t,
+  ;; extend all the way down to the leaves. If BOTTOM is a node, extend down
+  ;; as far as that node. If BOTTOM is an integer, extend down 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)
+       (cur-stack (list node))
+       next-stack)
+    ;; don't bother extending if BOTTOM specifies an already-drawn node
+    (unless (and (undo-tree-node-p bottom) (undo-tree-node-marker bottom))
+      ;; draw nodes layer by layer
+      (while (or cur-stack
+                (prog1 (setq cur-stack next-stack)
+                  (setq next-stack nil)))
+       (setq node (pop cur-stack))
+       ;; if node is within range being drawn...
+       (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)))
+           ;; ...draw one layer of node's subtree (if not already drawn)
+           (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)))
+         ;; ...otherwise, postpone drawing until later
+         (push node undo-tree-visualizer-needs-extending-down))))
+    extended))
+
+
+(defun undo-tree-extend-up (node &optional top)
+  ;; Extend tree upwards starting from NODE. If TOP is t, extend all the way
+  ;; to root. If TOP is a node, extend up as far as that node. If TOP is an
+  ;; 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)
+    ;; 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
+       (setq parent (undo-tree-node-previous node))
+       ;; if we haven't reached root...
+       (if parent
+           ;; ...and node is within range being drawn...
+           (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)
+                        ;; NOTE: check point in case window-start is outdated
+                        (< (min (line-number-at-pos (point))
+                                (line-number-at-pos (window-start)))
+                           (line-number-at-pos
+                            (undo-tree-node-marker node)))))
+               ;; ...and it hasn't already been drawn
+               (when (not (undo-tree-node-marker parent))
+                 ;; link parent node to its representation in visualizer
+                 (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))
+                 ;; draw subtree beneath parent
+                 (setq undo-tree-visualizer-needs-extending-down
+                       (nconc (delq node (undo-tree-draw-subtree parent))
+                              undo-tree-visualizer-needs-extending-down))
+                 (setq extended t))
+             ;; ...otherwise, postpone drawing for later and exit
+             (setq undo-tree-visualizer-needs-extending-up (when parent node)
+                   parent nil))
+
+         ;; if we've reached root, stop extending and add top margin
+         (setq undo-tree-visualizer-needs-extending-up nil)
+         (goto-char (undo-tree-node-marker node))
+         (undo-tree-move-up 1)  ; top margin
+         (delete-region (point-min) (line-beginning-position)))
+       ;; next iteration
+       (setq node parent)))
+    extended))
+
+
+(defun undo-tree-expand-down (from &optional to)
+  ;; Expand tree downwards. FROM is the node to start expanding from. Stop
+  ;; expanding at TO if specified. Otherwise, just expand visible portion of
+  ;; tree and highlight active branch from FROM.
+  (when undo-tree-visualizer-needs-extending-down
+    (let ((inhibit-read-only t)
+         node-list extended)
+      ;; extend down as far as TO node
+      (when to
+       (setq extended (undo-tree-extend-down from to))
+       (goto-char (undo-tree-node-marker to))
+       (redisplay t))  ; force redisplay to scroll buffer if necessary
+      ;; extend visible portion of tree downwards
+      (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)))
+       ;; highlight active branch in newly-extended-down portion, if any
+       (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)
+  ;; Expand tree upwards. FROM is the node to start expanding from, TO is the
+  ;; node to stop expanding at. If TO node isn't specified, just expand visible
+  ;; portion of tree and highlight active branch down to FROM.
+  (when undo-tree-visualizer-needs-extending-up
+    (let ((inhibit-read-only t)
+         extended node-list)
+      ;; extend up as far as TO node
+      (when to
+       (setq extended (undo-tree-extend-up from to))
+       (goto-char (undo-tree-node-marker to))
+       ;; simulate auto-scrolling if close to top of buffer
+       (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))))
+      ;; extend visible portion of tree upwards
+      (and undo-tree-visualizer-needs-extending-up
+          (undo-tree-extend-up undo-tree-visualizer-needs-extending-up)
+          (setq extended t))
+      ;; extend visible portion of tree downwards
+      (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))
+      ;; highlight active branch in newly-extended-up portion, if any
+      (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)
+  ;; Draw highlighted active branch below NODE in current buffer. Stop
+  ;; highlighting at END node if specified.
   (let ((stack (list node)))
-    ;; link node to its representation in visualizer
-    (unless (markerp (undo-tree-node-marker node))
-      (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))
     ;; draw active branch
     (while stack
       (setq node (pop stack))
-      (goto-char (undo-tree-node-marker node))
-      (setq node (undo-tree-draw-subtree node 'active))
-      (setq stack (append stack node)))))
+      (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)
@@ -3183,7 +3380,7 @@ signaling an error if file is not found."
   ;; is current node.
   (goto-char (undo-tree-node-marker node))
   (when undo-tree-visualizer-timestamps
-    (backward-char (/ undo-tree-visualizer-spacing 2)))
+    (undo-tree-move-backward (/ undo-tree-visualizer-spacing 2)))
 
   (let* ((undo-tree-insert-face (and (boundp 'undo-tree-insert-face)
                                     (or (and (consp undo-tree-insert-face)
@@ -3233,7 +3430,8 @@ signaling an error if file is not found."
 
 (defun undo-tree-draw-subtree (node &optional active-branch)
   ;; Draw subtree rooted at NODE. The subtree will start from point.
-  ;; If ACTIVE-BRANCH is non-nil, just draw active branch below NODE.
+  ;; If ACTIVE-BRANCH is non-nil, just draw active branch below NODE. Returns
+  ;; list of nodes below NODE.
   (let ((num-children (length (undo-tree-node-next node)))
         node-list pos trunk-pos n)
     ;; draw node itself
@@ -3249,10 +3447,10 @@ signaling an error if file is not found."
      ((= num-children 1)
       (undo-tree-move-down 1)
       (undo-tree-insert ?|)
-      (backward-char 1)
+      (undo-tree-move-backward 1)
       (undo-tree-move-down 1)
       (undo-tree-insert ?|)
-      (backward-char 1)
+      (undo-tree-move-backward 1)
       (undo-tree-move-down 1)
       (setq n (car (undo-tree-node-next node)))
       ;; link next node to its representation in visualizer
@@ -3263,18 +3461,18 @@ signaling an error if file is not found."
       ;; add next node to list of nodes to draw next
       (push n node-list))
 
-     ;; if node had multiple children, draw branches
+     ;; if node has multiple children, draw branches
      (t
       (undo-tree-move-down 1)
       (undo-tree-insert ?|)
-      (backward-char 1)
-      (setq trunk-pos (point))
+      (undo-tree-move-backward 1)
+      (move-marker (setq trunk-pos (make-marker)) (point))
       ;; left subtrees
-      (backward-char
+      (undo-tree-move-backward
        (- (undo-tree-node-char-lwidth node)
           (undo-tree-node-char-lwidth
            (car (undo-tree-node-next node)))))
-      (setq pos (point))
+      (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))
@@ -3288,7 +3486,7 @@ signaling an error if file is not found."
           (undo-tree-move-forward 1)
           (undo-tree-move-down 1)
           (undo-tree-insert ?/)
-          (backward-char 2)
+          (undo-tree-move-backward 2)
           (undo-tree-move-down 1)
           ;; link node to its representation in visualizer
           (unless (markerp (undo-tree-node-marker (car n)))
@@ -3302,7 +3500,7 @@ signaling an error if file is not found."
          (+ (undo-tree-node-char-rwidth (car n))
             (undo-tree-node-char-lwidth (cadr n))
             undo-tree-visualizer-spacing 1))
-        (setq pos (point)))
+        (move-marker pos (point)))
       ;; middle subtree (only when number of children is odd)
       (when (= (mod num-children 2) 1)
         (setq n (cdr n))
@@ -3312,7 +3510,7 @@ signaling an error if file is not found."
                            (undo-tree-node-next node))))
           (undo-tree-move-down 1)
           (undo-tree-insert ?|)
-          (backward-char 1)
+          (undo-tree-move-backward 1)
           (undo-tree-move-down 1)
           ;; link node to its representation in visualizer
           (unless (markerp (undo-tree-node-marker (car n)))
@@ -3326,9 +3524,9 @@ signaling an error if file is not found."
          (+ (undo-tree-node-char-rwidth (car n))
             (if (cadr n) (undo-tree-node-char-lwidth (cadr n)) 0)
             undo-tree-visualizer-spacing 1))
-        (setq pos (point)))
+        (move-marker pos (point)))
       ;; right subtrees
-      (incf trunk-pos)
+      (move-marker trunk-pos (1+ trunk-pos))
       (dotimes (i (/ num-children 2))
         (setq n (cdr n))
         (when (or (null active-branch)
@@ -3338,7 +3536,7 @@ signaling an error if file is not found."
           (goto-char trunk-pos)
           (undo-tree-insert ?_ (- pos trunk-pos 1))
           (goto-char pos)
-          (backward-char 1)
+          (undo-tree-move-backward 1)
           (undo-tree-move-down 1)
           (undo-tree-insert ?\\)
           (undo-tree-move-down 1)
@@ -3355,13 +3553,12 @@ signaling an error if file is not found."
            (+ (undo-tree-node-char-rwidth (car n))
               (if (cadr n) (undo-tree-node-char-lwidth (cadr n)) 0)
               undo-tree-visualizer-spacing 1))
-          (setq pos (point))))
+          (move-marker pos (point))))
       ))
     ;; return list of nodes to draw next
     (nreverse node-list)))
 
 
-
 (defun undo-tree-node-char-lwidth (node)
   ;; Return left-width of NODE measured in characters.
   (if (= (length (undo-tree-node-next node)) 0) 0
@@ -3406,10 +3603,20 @@ signaling an error if file is not found."
     (setq line (line-number-at-pos))
     ;; if buffer doesn't have enough lines, add some
     (when (/= line (+ row arg))
-      (insert (make-string (- arg (- line row)) ?\n)))
+      (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)
+  ;; Move up, extending buffer if necessary.
+  (unless arg (setq arg 1))
+  (undo-tree-move-down (- arg)))
+
+
 (defun undo-tree-move-forward (&optional arg)
   ;; Move forward, extending buffer if necessary.
   (unless arg (setq arg 1))
@@ -3424,19 +3631,73 @@ signaling an error if file is not found."
      ((< arg 0)
       (setq arg (- arg))
       (setq n (- (point) (line-beginning-position)))
-      (when (< n arg)
+      (when (< (- n 2) arg)  ; -2 to create left-margin
        ;; no space left - shift entire buffer contents right!
-       (let ((pos (make-marker)))
+       (let ((pos (move-marker (make-marker) (point))))
          (set-marker-insertion-type pos t)
-         (move-marker pos (point))
          (goto-char (point-min))
          (while (not (eobp))
-           (insert (make-string (- arg n) ? ))
+           (insert-before-markers (make-string (- arg -2 n) ? ))
            (forward-line 1))
          (goto-char pos)))
       (backward-char arg)))))
 
 
+(defun undo-tree-move-backward (&optional arg)
+  ;; Move backward, extending buffer if necessary.
+  (unless arg (setq arg 1))
+  (undo-tree-move-forward (- arg)))
+
+
+(defun undo-tree-move-to-parent (node)
+  ;; Move to position of parent of NODE, extending buffer if necessary.
+  (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)
+      ;; move horizontally
+      (setq p (undo-tree-position node n))
+      (cond
+       ;; node in centre subtree: no horizontal movement
+       ((and (= (mod l 2) 1) (= p (/ l 2))))
+       ;; node in left subtree: move right
+       ((< 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 ;; node in right subtree: move left
+       (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)))))
+    ;; move vertically
+    (undo-tree-move-up 3)))
+
+
 (defun undo-tree-timestamp-to-string
   (timestamp &optional relative current register)
   ;; Convert TIMESTAMP to string (either absolute or RELATVE time), indicating
@@ -3484,7 +3745,7 @@ signaling an error if file is not found."
 
 
 ;;; =====================================================================
-;;;                    Visualizer mode commands
+;;;                        Visualizer commands
 
 (defun undo-tree-visualizer-mode ()
   "Major mode used in undo-tree visualizer.
@@ -3513,34 +3774,51 @@ Within the undo-tree visualizer, the following keys are 
available:
 (defun undo-tree-visualize-undo (&optional arg)
   "Undo changes. A numeric ARG serves as a repeat count."
   (interactive "p")
-  (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-undo arg))
-    (switch-to-buffer-other-window undo-tree-visualizer-buffer-name)
-    (let ((inhibit-read-only t))
-      (undo-tree-draw-node (undo-tree-current buffer-undo-tree) 'current))
-    (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff))))
+  (let ((old (undo-tree-current buffer-undo-tree))
+       current)
+    ;; unhighlight old current node
+    (let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face)
+         (inhibit-read-only t))
+      (undo-tree-draw-node old))
+    ;; undo in parent buffer
+    (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 using lazy drawing, extend tree upwards as required
+      (when undo-tree-visualizer-lazy-drawing
+       (undo-tree-expand-up old current))
+      ;; highlight new current node
+      (let ((inhibit-read-only t)) (undo-tree-draw-node current 'current))
+      ;; update diff display, if any
+      (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")
-  (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 arg))
-    (switch-to-buffer-other-window undo-tree-visualizer-buffer-name)
-    (goto-char (undo-tree-node-marker (undo-tree-current buffer-undo-tree)))
-    (let ((inhibit-read-only t))
-      (undo-tree-draw-node (undo-tree-current buffer-undo-tree) 'current))
-    (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff))))
+  (let ((old (undo-tree-current buffer-undo-tree))
+       current)
+    ;; unhighlight old current node
+    (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)))
+    ;; redo in parent buffer
+    (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 using lazy drawing, extend tree downwards as required
+      (when undo-tree-visualizer-lazy-drawing
+       (undo-tree-expand-down old current))
+      ;; highlight new current node
+      (let ((inhibit-read-only t)) (undo-tree-draw-node current 'current))
+      ;; update diff display, if any
+      (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff)))))
 
 
 (defun undo-tree-visualize-switch-branch-right (arg)
@@ -3581,7 +3859,7 @@ using `undo-tree-redo' or `undo-tree-visualizer-redo'."
 (defun undo-tree-visualizer-quit ()
   "Quit the undo-tree visualizer."
   (interactive)
-  (undo-tree-clear-visualizer-data (undo-tree-root buffer-undo-tree))
+  (undo-tree-clear-visualizer-data buffer-undo-tree)
   ;; remove kill visualizer hook from parent buffer
   (unwind-protect
       (with-current-buffer undo-tree-visualizer-parent-buffer
@@ -3619,9 +3897,7 @@ at POS, or point if POS is nil."
       (let ((undo-tree-inhibit-kill-visualizer t)) (undo-tree-set node))
       (switch-to-buffer-other-window undo-tree-visualizer-buffer-name)
       ;; re-draw undo tree
-      (let ((inhibit-read-only t))
-       (undo-tree-draw-tree (undo-tree-root buffer-undo-tree)
-                            (undo-tree-current buffer-undo-tree)))
+      (let ((inhibit-read-only t)) (undo-tree-draw-tree buffer-undo-tree))
       (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff)))))
 
 
@@ -3709,19 +3985,55 @@ a negative prefix argument specifies `register'."
   (setq undo-tree-visualizer-timestamps (not undo-tree-visualizer-timestamps))
   (setq undo-tree-visualizer-spacing (undo-tree-visualizer-calculate-spacing))
   ;; redraw tree
-  (let ((inhibit-read-only t))
-    (undo-tree-draw-tree (undo-tree-root buffer-undo-tree)
-                        (undo-tree-current buffer-undo-tree))))
+  (let ((inhibit-read-only t)) (undo-tree-draw-tree buffer-undo-tree)))
 
 
 (defun undo-tree-visualizer-scroll-left (&optional arg)
   (interactive "p")
-  (scroll-right (or arg 1) t))
+  (scroll-left (or arg 1) t))
 
 
 (defun undo-tree-visualizer-scroll-right (&optional arg)
   (interactive "p")
-  (scroll-left (or arg 1) t))
+  (scroll-right (or arg 1) t))
+
+
+(defun undo-tree-visualizer-scroll-up (&optional arg)
+  (interactive "P")
+  (if (or (and (numberp arg) (< arg 0)) (eq arg '-))
+      (undo-tree-visualizer-scroll-down arg)
+    ;; scroll up and expand newly-visible portion of tree
+    (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)))))
+    ;; signal error if at eob
+    (when (and (not undo-tree-visualizer-needs-extending-down) (eobp))
+      (scroll-up))))
+
+
+(defun undo-tree-visualizer-scroll-down (&optional arg)
+  (interactive "P")
+  (if (or (and (numberp arg) (< arg 0)) (eq arg '-))
+      (undo-tree-visualizer-scroll-up arg)
+    ;; ensure there's enough room at top of buffer to scroll
+    (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)))))
+    ;; scroll down and expand newly-visible portion of tree
+    (unwind-protect
+       (scroll-down-command arg)
+      (undo-tree-expand-up
+       (undo-tree-node-previous (undo-tree-current buffer-undo-tree))))
+    ;; signal error if at bob
+    (when (and (not undo-tree-visualizer-needs-extending-down) (bobp))
+      (scroll-down))))
 
 
 
@@ -3753,10 +4065,15 @@ a negative prefix argument specifies `register'."
       (dotimes (i arg)
        (unless (undo-tree-node-previous node) (throw 'top t))
        (setq node (undo-tree-node-previous node))))
-    (goto-char (undo-tree-node-marker node))
+    ;; when using lazy drawing, extend tree upwards as required
+    (when undo-tree-visualizer-lazy-drawing
+      (undo-tree-expand-up 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))
+    ;; move to selected node
+    (goto-char (undo-tree-node-marker node))
     (setq undo-tree-visualizer-selected-node node)))
 
 
@@ -3770,10 +4087,15 @@ a negative prefix argument specifies `register'."
          (throw 'bottom t))
        (setq node
              (nth (undo-tree-node-branch node) (undo-tree-node-next node)))))
-    (goto-char (undo-tree-node-marker node))
+    ;; when using lazy drawing, extend tree upwards as required
+    (when undo-tree-visualizer-lazy-drawing
+      (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))
+    ;; move to selected node
+    (goto-char (undo-tree-node-marker node))
     (setq undo-tree-visualizer-selected-node node)))
 
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]