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?

  • 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? – DoMiNeLa10 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

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)))
    (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)
      (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))
            (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)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.