5

C-x u aka undo-tree-visualize enables a user visualize the undo tree history of a buffer.

I love undo tree, and I want to have it displayed all the time as a visual map of my changes. I would like it to always be there in a buffer, that is, always open and keeping track of whatever butter I am working in. Then, it will change in real time as I update a buffer (it already does this) but if I displayed the undo tree in its own window, I will see it change dynamically as I switch between buffers. I am relatively new to emacs and have no idea how to do this. Any pointers?

Wapiti
  • 151
  • 3
  • 2
    "I would like it to always be there in a buffer" is pretty vague, and I assume you want it to display in another window and update in real time. Can you update your question to be a bit more specific? –  Dec 17 '18 at 15:23
  • 1
    With respect to the visualization buffer, it is possible to have it displayed all the time and update every command loop. However, the slow-down in Emacs performance would be noticeable even with lazy drawing enabled -- especially as the undo-tree-list grows in size. I would be hesitant to write-up such a solution based upon that knowledge. I have sometimes have an undo-tree-history (when saved to a file) that is 1 to 2 MB in size and it is simply not practical to open the visualization buffer for such a large history of edits. – lawlist Dec 17 '18 at 16:12
  • That is a good point, but I anticipate using such a thing for all open buffers, but on a per-session basis, not saving all changes for all sessions. My workflow is generally to work on a file for a few hours at a time before closing it. Does that address your concerns? – Wapiti Dec 17 '18 at 18:32
  • @DoMiNeLa10 I edited slightly to try to make it more clear. But yes, your assumption is right. – Wapiti Dec 17 '18 at 18:35
  • 1
    Let us assume that we quickly type the word "hello-world". There are eleven characters, with one command loop for each character. In my use case, I have done away with amalgamation of undo and every character typed is one nodule/node in the undo-tree visualization buffer. If we update the visualization buffer for every character typed, then Emacs must momentarily focus on the visualization buffer between typing each character. We might have `flyspell` mode in the working buffer. We might have fancy font-lock stuff happening. Typing "hello-world" may be slow due to undo visualization.... – lawlist Dec 17 '18 at 19:03
  • Ugh, that is not good. I guess that's probably why I can't keep the undo tree visualization open between buffers! – Wapiti Dec 17 '18 at 19:23
  • @lawlist maybe you want to edit your comments and put them in an answer in case someone else has the same idea? – Wapiti Dec 17 '18 at 19:47

1 Answers1

2

To avoid performance issues, it is not recommended to run undo-tree-update-visualization-buffer on the post-command-hook -- thus, here we use run-with-idle-timer instead. To try this out, place undo-tree.el/elc in the load-path, then paste the following code in a *scratch* buffer and type M-x eval-buffer

(require 'undo-tree)

(defun undo-tree-update-visualization-buffer ()
  (let ((update-visualization-buffer nil)
        (tree nil)
        (selected-window (selected-window))
        (current-buffer (current-buffer)))
    (when (and undo-tree-mode (not (get-buffer undo-tree-visualizer-buffer-name)))
      (undo-tree-visualize))
    (when (and undo-tree-mode (not (get-buffer-window undo-tree-visualizer-buffer-name)))
      (let ((display-buffer-mark-dedicated 'soft))
        (display-buffer undo-tree-visualizer-buffer-name)))
    (when undo-tree-mode
      (when (memq 'undo-tree-kill-visualizer before-change-functions)
        (remove-hook 'before-change-functions 'undo-tree-kill-visualizer 'local))
      (unless (equal buffer-undo-list '(nil undo-tree-canary))
        (setq update-visualization-buffer t)
        (undo-list-transfer-to-tree))
      (setq tree buffer-undo-tree)
      (with-current-buffer undo-tree-visualizer-buffer-name
        (unless (eq undo-tree-visualizer-parent-buffer current-buffer)
          (setq update-visualization-buffer t)
          (setq undo-tree-visualizer-parent-buffer current-buffer)
          (setq undo-tree-visualizer-parent-mtime
                  (and (buffer-file-name current-buffer)
                        (nth 5 (file-attributes (buffer-file-name current-buffer)))))
          (setq undo-tree-visualizer-initial-node (undo-tree-current tree))
          (setq undo-tree-visualizer-spacing (undo-tree-visualizer-calculate-spacing)))
        (when update-visualization-buffer
          (let ((inhibit-read-only t))
            (erase-buffer)
            (setq buffer-undo-tree tree)
            (undo-tree-draw-tree tree)
            (goto-char (point-max))
            (when (re-search-backward "x" nil t)
              (set-window-point (get-buffer-window undo-tree-visualizer-buffer-name) (point)))))))
    (unless (eq selected-window (selected-window))
      (select-window selected-window))))

(run-with-idle-timer 1.5 'repeat #'undo-tree-update-visualization-buffer)
lawlist
  • 18,826
  • 5
  • 37
  • 118