Git Product home page Git Product logo

vundo's Introduction

Vundo is now available on ELPA!

Vundo (visual undo) displays the undo history as a tree and lets you
move in the tree to go back to previous buffer states. To use vundo,
type M-x vundo RET in the buffer you want to undo. An undo tree buffer
should pop up. To move around, type:

  f   to go forward
  b   to go backward

  n   to go to the node below when at a branch point
  p   to go to the node above

  a   to go back to the last branching point
  w   to go forward to the next branching point
  e   to go forward to the end/tip of the branch
  l   to go to the last saved node
  r   to go to the next saved node

  m   to mark the current node for diff
  u   to unmark the marked node
  d   to show a diff between the marked (or parent) and current nodes

  q   to quit, you can also type C-g

  C-c C-s (or whatever binding you used for save-buffer)
      to save the buffer at the current undo state

n/p may need some more explanation. In the following tree, n/p can
move between A and B because they share a parent (thus at a branching
point), but not C and D. To make it clear, branches you can switch
between are highlighted with bold face.

         A  C
    ──○━━○──○──○──○
      ┃  ↕︎
      ┗━━○──○──○
         B  D

By default, you need to press RET to “commit” your change and return
to the buffer.  If instead you quit with q or C-g, the changes made by
vundo are rolled back. You can set ‘vundo-roll-back-on-quit’ to nil to
disable rolling back.

You might see some green nodes in the tree, those are the buffer
states that have been saved to disk; the last saved node is emphasized
in bold. You can type "l" to jump to the last saved node.

Note: vundo.el requires Emacs 28.

Customizable faces:

- vundo-default
- vundo-node
- vundo-stem
- vundo-highlight
- vundo-saved
- vundo-last-saved

If you want to use prettier Unicode characters to draw the tree like
this:

    ○──○──○
    │  └──●
    ├──○
    └──○

set vundo-glyph-alist by

    (setq vundo-glyph-alist vundo-unicode-symbols)

Your default font needs to contain these Unicode characters, otherwise
they look terrible and don’t align. You can find a font that covers
these characters (eg, Symbola, Unifont), and set ‘vundo-default’ face
to use that font:

    (set-face-attribute 'vundo-default nil :family "Symbola")

Diff:

Vundo uses Emacs' facilities to provide diffs among arbitrary undo
states: just (m)ark and (d)iff.  By default, vundo's diff window is
buried when vundo quits; see `vundo-diff-quit' for other options.

Terminal users may encounter unwanted control characters in the diff
output.  Emacs colors diff buffers itself, so this can be remedied by
instructing diff not to print color codes:

    (setq diff-switches "-u --color=never")

Comparing to undo-tree:

Vundo doesn’t need to be turned on all the time nor replace the undo
commands like undo-tree does. Vundo displays the tree horizontally,
whereas undo-tree displays a tree vertically.  Diff is provided
on-demand between any nodes.

Tests:

You can run tests by loading test/vundo-test.el and M-x ert RET t RET
to run those tests interactively, or use the following batch command:

     emacs --batch \
           -l vundo.el \
           -l test/vundo-test.el \
           -f ert-run-tests-batch-and-exit


Changelog (full changelog in NEWS.txt):

<2023-02-16 Fri>: Version 2.3.0: navigate among all saved nodes.
Automatically bury the vundo-diff window when vundo quits.

<2023-12-17 Sun>: Version 2.2.0: vundo-diff introduced, supporting
on-demand diff to parent or any marked node.  Improved tree draw
speed.

<2022-04-04 Mon>: Version 1.0.0

<2022-03-29 Tue>: vundo--mode and vundo--mode-map are now vundo-mode
and vundo-mode-map. A new custom option vundo-compact-display is added.

<2022-03-23 Wed>: UI now defaults to ASCII mode. ASCII mode also draws
differently now, it now draws

    o--o--o     instead of      o--o--o
    |  `--x                     |  +--*
    |--o                        |--o
    `--o                        +--o

<2021-11-26 Fri>: Variable vundo-translate-alist changed to
vundo-glyph-alist and has different value now.

vundo's People

Contributors

45mg avatar casouri avatar damiencassou avatar gabor-braun avatar ideasman42 avatar jdtsmith avatar nasyxx avatar phikal avatar protesilaos avatar wyuenho avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vundo's Issues

Assertion with undo steps that only move the cursor

Very ocasionally vundo is asserting:

cl--assertion-failed: Assertion failed: (not (and (consp buffer-undo-list) (null (car buffer-undo-list))))

I traced the problem down to undo steps that only move the cursor, (an undo step that contains a single integer representing the cursor position).

Here is a script to reproduce the issue:

(defun undo-test-only-positional-changes ()
  "Use this"
  (interactive)
  (dotimes (n 10)
    (push n buffer-undo-list)
    (push nil buffer-undo-list)))

(global-set-key (kbd "<f12>") 'undo-test-only-positional-changes)

Run this on a buffer with 11 characters or more, then undo (the cursor moves).
Then try use vundo, it instantly asserts.


Having undo steps that only move the cursor position seems strange, it's possible this occurs because of logic in undo-fu-session that flattens the undo-history. Although attempting to create branching steps that are flattened - I can't force this to happen, so I'm not sure why this occurs. Nevertheless, since it seems like valid undo data that emacs undo supports, I think vundo could support it too.

skipping some redos with/without vundo?

I was using vundo today to unravel a mystery of an edit that happened somehow, and it helped me identify which edit had the problem.

Now I have even a tricker part would like to solve: redo all the rest of the edits, but this problematic one.

Would this be a possibility with vundo? I feel like this could be an interesting idea.

Me myself is thinking, it may be an better idea to create a new undo branch, and not directly changing the undo history, and I also do not know how to make that happen yet. Would appreciate for all advises.

Lastly, I would most certainly apreciate some feedback some this idea also!
Thanks!

Test `vundo-test--3` fails in emacs-29

emacs -batch -l ./vundo.el -l ./test/vundo-test.el -f ert-run-tests-batch-and-exit

Gives the following error in emacs-29 (9fab134ee8b4ed439a8944e0d7058b1898c9bc0b).

Running 7 tests (2022-04-04 10:15:43+1000, selector ‘t’)
Undo
   passed  1/7  vundo-test--1 (0.011325 sec)
   passed  2/7  vundo-test--2 (0.118555 sec)
Mark set
Undo
Test vundo-test--3 backtrace:
  ert-fail(((should (equal (vundo-test--buf-str-np) "acde")) :form (eq
  (if (unwind-protect (setq value-137 (apply fn-135 args-136)) (setq f
  (let (form-description-139) (if (unwind-protect (setq value-137 (app
  (let ((value-137 'ert-form-evaluation-aborted-138)) (let (form-descr
  (let* ((fn-135 #'equal) (args-136 (condition-case err (let ((signal-
  (let ((vundo-glyph-alist vundo-unicode-symbols)) (vundo-test--insert
  (progn (buffer-enable-undo) (let ((vundo-glyph-alist vundo-unicode-s
  (unwind-protect (progn (buffer-enable-undo) (let ((vundo-glyph-alist
  (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn
  (let ((temp-buffer (generate-new-buffer " *temp*" t))) (save-current
  (closure (t) nil (let ((temp-buffer (generate-new-buffer " *temp*" t
  ert--run-test-internal(#s(ert--test-execution-info :test #s(ert-test
  ert-run-test(#s(ert-test :name vundo-test--3 :documentation "This te
  ert-run-or-rerun-test(#s(ert--stats :selector t :tests [#s(ert-test
  ert-run-tests(t #f(compiled-function (event-type &rest event-args) #
  ert-run-tests-batch(nil)
  ert-run-tests-batch-and-exit()
  command-line-1(("-l" "./vundo.el" "-l" "./test/vundo-test.el" "-f" "
  command-line()
  normal-top-level()
Test vundo-test--3 condition:
    (ert-test-failed
     ((should
       (equal
        (vundo-test--buf-str-np)
        "acde"))
      :form
      (equal "abcd" "acde")
      :value nil :explanation
      (array-elt 1
                 (different-atoms
                  (98 "#x62" "?b")
                  (99 "#x63" "?c")))))
   FAILED  3/7  vundo-test--3 (0.000103 sec) at test/vundo-test.el:183
   passed  4/7  vundo-test--4 (1.562135 sec)
   passed  5/7  vundo-test--mod-list (0.000068 sec)
   passed  6/7  vundo-test--position-only-p (0.000032 sec)
   passed  7/7  vundo-test--skip-position-only (0.000027 sec)

Ran 7 tests, 6 results as expected, 1 unexpected (2022-04-04 10:15:44+1000, 1.789043 sec)

1 unexpected results:
   FAILED  vundo-test--3

some issue from evil-mode

I'm a user of evil-mode. So I prefer to call Vundo when I use evil-undo-system. This is my code below:

(defun evil-undo-advice(fn &rest _)
  "Hybrid evil and vundo."
  (if (not (fboundp 'vundo))
      (funcall fn 1)
    (vundo)
    (if (eq fn 'evil-redo)
        (vundo-forward 1)
      (vundo-backward 1))))

(advice-add 'evil-undo :around #'evil-undo-advice)
(advice-add 'evil-redo :around #'evil-undo-advice)

The problem is orig-function arguments fn , (if (eq fn 'evil-redo) always return nil , even I call evil-redo , it still execute vundo-backward , I know it's not Vundo's problem, but how should I fix it ?

A possible approach to the "lost timestamp" issue

Recap of the issue

A recap from #62: there is one type of information that is lost when vundo trims the "useless" tails of very long undo-buffer lists — timestamps. Emacs inserts timestamps into the modification record when modifying a saved (aka unmodified) buffer. These timestamp (TS) records are simple, and look like:

 (t 25616 45086 191805 680000) ; the numbers are the same format as (current-time)

Importantly, timestamps are inserted whether the modification in question is an "ordinary" modification, or an "undo/redo" modification. But vundo trims the latter to avoid the buffer-undo-list growing exponentially (which happens fast thanks to its ability to fly around a tree of undos). In doing this trimming, timestamps associated with undo/redo modifications are lost.

Impact

These lost timestamps show up in two ways:

  1. vundo and regular undo/redo differ in their ability to correctly mark a buffer as unmodified on returning to that state; vundo cannot always recognize when it has returned to an unmodified buffer state because of this lossage. undo/redo never fails.
  2. The new saved-state indication from #62 does not work when you undo to some older state, save it, then undo/redo away from it. The TS that is introduced from this is currently culled by vundo, no trace remaining.

Potential Solution

Here is a possible solution: for any new TS records found past the last ordinary modification, "teleport" them back to the equivalent node closest to the root with the same directionality. Said more precisely: for t representing the newly discovered node with the timestamp, pair (t-1, t) is mapped to equivalent pair (p-1, p), for the minimal p. Then we teleport the TS from tp, overwriting any found there.

Diagram

In picture form (borrowing from @casouri's excellent blog post):

image

Outcome

To evaluate if a given node is saved, we check if the next state of that node or any equivalent node has a TS record. For example, in the diagram, node 1 is known to be saved, because its equivalent node 5 sees a TS in the next state up (node 6). This will fix problem 2 above.

Problem 1 is also fixed, as long as vundo selects a path that arrives via the adjacent TS node. So for example, in the above right-hand example, going from 0→1 is via 6→5, which works correctly. No path from 2→1 will set the buffer to unmodified, but the same would be true for regular undo/redo as well. Also note that the teleportation process, even if it overwrites an older TS, does not cause problems; undo already ignores older timestamps that do not match the file's modification time.

Cost

The cost is (sometimes) maintaining a bit more tail to track save-then-undo's. In the above example (pink node is current), normally you'd trim back to node 5. For the left scenario, you do that. For the right scenario, you trim only back to node 6.

[FR/Suggestion] vundo-forward and backward bring up vundo buffer

Hello,

I uses evil, and u & C-r keys are for undo and redo for me respectively.

Right now, it doesn't seem it is possible to bring up the vundo buffer and undo or redo right away.

If in the future there is to be integration with the evil-undo-system, IMO, it would then be beneficial to implement something like this.
Such that the following would be possible:

(setq evil-undo-function #'vundo-forward
        evil-redo-function #'vundo-backward)

What is your recommendation for this?

Thank you

Make release 2.5.0 official

Hi
Can you please make 2.5.0 release official so that it can be picked by elpa

Elpa is still using 2.1.0

Thanks !

Suggestions for changed in behavior for next/previous

From reddit user nv-elisp.


Pretty cool. I find the graph easier to follow horizontally than the vertical orientation undo-tree has.

A few ideas/suggestions:

I would expect vundo-next to work here as well and follow the diagonal of the graph. From here:

o--x--o
   `--o  

to here:

o--o--o
   `--x  

And I would expect vundo-previous to work similarly:

o--o--o
   `--x  

would lead to here:

o--x--o
   `--o  

Basically follow the lines of the graph when pressed instead of hopping to the the parallel point in the graph. This was just my initial impression, though. I could see getting used to the way it currently works as well.

It would be neat if one could click on any of the unselected points in the graph and show a buffer diffing current position in the graph with that point.

Another idea is to record/display some information about each point in the graph when navigating. Perhaps the time the edit was made?

Food for thought.

[PR] more compat with evil and visual-line-mode

You might want to incorporate this, or the package will be very unpleasant for people with other configs:

  (defun night/h-vundo-buf-setup (orig-fn &rest args)
    (let ((vundo-buf (apply orig-fn args)))
      (with-current-buffer vundo-buf
        (toggle-truncate-lines 1)
        (evil-insert-state))
      vundo-buf))
  (advice-add 'vundo-1 :around #'night/h-vundo-buf-setup)

Setting to Unicode Fails in init

In my init I have the (setq vundo-glyph-alist vundo-unicode-symbols) which fails and if I try evalutating it, it fails as well. Unless I run vundo and then evaluate it, then it works just fine. I then tried putting (vundo-mode) in my init before the setq command and that also fails. It seems like the issue is that until I call vundo I can't call vundo-mode or change the variables.

When I launch Emacs I have 4 vundo commands available in M-x, vundo, vundo-diff, vundo-diff-mark and vundo-diff-unmark. After calling vundo though I get 19 different commands. So it seems like somehow vundo isn't being loaded properly?

I am using Emacs 29.1 with the most up to date version of vundo.

Love this package, thank you!

Great package. Thank you for making it! I particularly love

  1. tighter integration with Emacs' native history (it's only a matter of time before I have to code something in my config that needs to intimately manipulate/abuse history entries, in fact the prospect of doing that is what got me looking at vundo again, once I started to suspect I might have to couple my code with undo-tree (or Evil, which in turn already couples to undo-tree when both are in use) for the thing I'm trying to do), and
  2. the horizontal and compact view (I use Emacs extensively on my phones!),

Love the option to use pretty unicode characters too.

I haven't switched from undo-tree yet, but looking at how the feature set has matured and caught up (diffs, for example) has me seriously excited about it once I start wanting any of the three benefits I just mentioned badly enough. Literally right after making this post I tried switching. My config is now simpler and for the most part things are better.

BUG: duplicates on `vundo--timestamps`

The simplifying "cleanup" fix e5f3b81 introduced a subtle bug: it now allows duplicate entries to appear on vundo--timestamps, which is an alist (where only "first match" counts) but is also a time-sorted list, for which duplicates do matter. In some situations this defeats previous/next saved node navigation, since the current and target node are identical.

Update: Hotfix in #102.

jit-lock errors

I'm getting jit-lock errors every time I run vundo:

Error during redisplay: (jit-lock-function 1) signaled (wrong-type-argument number-or-marker-p nil)

Running vundo ecc0e2c, emacs -Q, tested with Emacs 28 and 29.

The errors disappear when I deactivate jit-lock-mode via vundo-mode-hook.

I also noticed that, in the definition of vundo-mode, jit-lock-mode is called like this:

(jit-lock-mode -1)

Perhaps you meant to deactivate jit-lock-mode there? If so, this is the correct (and unusual for minor-modes) form:

(jit-lock-mode nil)

Vundo can be called inside a read-only buffer

Hej Hej,

More of a question than an issue:
Is it intended that vundo can be called inside a read-only buffer?
If it is shouldn't the read-only state be restored after exiting vundo?

My first thoughts would be "no" and "yes", but maybe I am missing something.

Cheers and thanks for the package,
OlMon

Horizontal orientation usability

A couple of issues I have noticed with the horizontal orientation of the tree:

  1. For a long chain of changes, the last item in the chain doesn't show upon loading the vundo buffer in my setup, it requires a back-and-forth movement (notice the key cast in the modeline):

output-2022-03-25-17:16:36

  1. A related problem, somewhat exacerbated by turning off the mode-line (which I patched for the screencast above, to show the keycast), is that I have no context on where I am in the tree, close to the beginning or the end.

Error calling vundo-stem-root at the beginning of the undo history

When at the beginning of the undo history run vundo-stem-root, this results in an error.

Debugger entered--Lisp error: (wrong-type-argument vundo-m nil)
  vundo--eqv-list-of(nil)
  vundo--calculate-shortest-route(#s(vundo-m :idx 0 :children ... <SNIP>
  vundo--move-to-node(#s(vundo-m :idx 0 :children ... :parent nil :prev-eqv nil :next-eqv ... :undo-list nil :point 2) nil #<buffer creator.c> (... ... ... ... ... ... ... ... ... ... ... ... ... ... ...))
  vundo-stem-root()
  funcall-interactively(vundo-stem-root)
  command-execute(vundo-stem-root)

Edit, this also happens running vundo-stem-root when at the stem end too.

Debugger entered--Lisp error: (void-function vundo-diff--quit)

I'm not sure how to properly install vundo,

I have this set up in elpaca:

;; Emulates vim keybinds & modal editing
(use-package evil
  :init
  ;; ...
  :config
  (evil-mode 1))

;; keep undo history
(use-package undo-fu
  :config
  (setq evil-undo-system 'undo-fu))

;; keep file's undo history between emacs sessions
(use-package undo-fu-session
  :config
  (undo-fu-session-global-mode))

;; make undo history a tree on-the-fly
(use-package vundo)

Every time I enter vundo using M-x, pressing <RET> returns this error,

Debugger entered--Lisp error: (void-function vundo-diff--quit)
  vundo-diff--quit()
  vundo-confirm()
  funcall-interactively(vundo-confirm)
  command-execute(vundo-confirm)

And it seems like the vundo-diff.el isn't getting loaded how hard I try, I couldn't see those functions in help page, but I do see those files cloned and compiled in the elpaca cache.

I tried different ways elpaca support

(use-package vundo
  :ensure (vundo :files (:defaults "*.el")))

;; or
(use-package vundo
  :ensure (vundo :files ("vundo.el" "vundo-diff.el")))

;; or
(use-package vundo
  :ensure (vundo :host github :repo "casouri/vundo" :files (:defaults "*.el")))

but none seem to work.

Thanks.

Also, can installation instructions be added in the README? 👀

keybinding for going back to latest change

Maybe I'm just bad at looking but I couldn't find a keybinding that takes me back to where I was before I did M-x vundo (or perhaps to the last change, I guess they can be different), is there one?

Highlighting nodes of saves - extend?

Hello,

to get the nodes corresponding to saves highlighted - do I have to save from the vundo buffer to get this?

Because I am using vundo-highlight-saved-nodes -> t I and never saw such highlights. Would you consider to allow to get the highlights when the user is saving normally?

I made it work in my config, but I had to duplicate a lot of quite internal vundo code. It would be nice if you could factor out a function that does the job. So that one could use it in or as an advice of save-buffer. Would that make sense?

TIA - Michael.

Package on Melpa

I really like the package and recommend it to others. For me personally, I use straight.el and pull the package directly from GitHub. However for new emacs users who are not ready for straight or loading a package manually, packaging this on Melpa would be very helpful. Do you have any plans to do this?

diff not working in some buffers

Hi,

I have tried vundo, but the diff feature didn't work properly for me. It does work in init.el buffer, but it doesn't work in 15.py - the diff is not shown at all:
EmacsPgU29p

After a bit of digging, I have found that the problem is caused by vundo-diff--cleanup-diff-buffer - before it is called, the diff is shown.
Seems like an issue with the regexp:

(rx-to-string `(and bol ,pat (+ space)
		    (group (group (+ (not ?\t)))
			   (* any))
		    eol))

In particular, when I replace the greedy operators + and * by their non-greedy variant:

(rx-to-string `(and bol ,pat (+ space)
		    (group (group (+? (not ?\t)))
			   (*? any))
		    eol))

The diff is shown nicely:
Emacs5eP2WP

I don't know why this only happens in some buffers.

Exiting vundo should (optionally) exit vundo-diff

It would be good if quitting vundo would also close the vundo-diff buffer & window.
So viewing a vundo-diff buffer doesn't leave this buffer open once I've finished using vundo (which I need to manually exit). I'm not so fussed on this being on by default or not, just that it would be nice if it's a toggle.

Of course it's possible to write advice that does this but it seems like something users might want to enable without the hassle of implementing this themselves.

Note that vundo-post-exit-hook can't easily be used for this as the vundo buffer has been deleted (which is needed to access the name of the diff buffer).


This advice for vundo-quit works but it involves using internal names.

(advice-add
 'vundo-quit
 :around
 #'(lambda (old-fn &rest args)
     (let* ((orig vundo--orig-buffer)
            (oname (buffer-name orig))
            (diff-buf (get-buffer (concat "*vundo-diff-" oname "*"))))
       (when diff-buf
         (let ((diff-window (get-buffer-window diff-buf)))
           (when diff-window
             (delete-window diff-window))
           (kill-buffer diff-buf)))

       (let ((result (apply old-fn args)))
         result))))

Make forward remember which branch back came from?

One UX nicety of undo-tree is that if you hit back to a branch point, and then hit forward, you go forward in whatever branch you were just on.

undo-tree visualizes this by coloring the currently "selected" branch slightly differently.

Is it feasible/realistic to have that in vundo?

Currently, as a user used to undo-tree, this feels like a big efficiency loss, because I am so used to being able to toggle between any two adjacent nodes by just hitting by back/forward keys (my thinking and muscle memory doesn't have to be predicated on whether or not I'm just in front of a branch boundary).

However, as I get used to vundo's capabilities, I may stop wanting this:

  1. Rapidly cycling between two or nodes is something I do to think (but maybe I can use the vundo-diff buffer instead for that, especially because vundo-diff can do diffs to any marked node (wonderful feature by the way!)).
  2. I feel like undo-tree's visual indicator of which branch is "selected" helps me navigate over large sets of changes without losing my place. But maybe it'll turn out I can comfortably do without.

So I'm not advocating for this to have high priority. But it seems like a nicety worth adding if it's fairly easy.

Stress-testing vundo

See this gist for an undo/vundo stress-test function, vundo--stress-test. It has been revealing to play with.

These setting definitely pushing the limits:

Undo stress test complete:
	10000 sentences inserted
	1500 undo chains of max length 12

	buffer-undo-list = 1948846 bytes
Buffer has 6394 lines, 39009 words, and 278099 characters.
Launching vundo: 
Elapsed time: 9.235434s (0.088656s in 1 GCs)
Mark set

this one ranges from 5-10s (6s more typical). Even with this gargantuan tree, navigation is still quite smooth (again, 8 yo hardware here). Using a to head up the tree works reliably, even as the length of the buffer-undo-list grows beyond 1/2 million. This is really impressive @casouri.

You can start to break things by going to extremes (here I've added a navigation to root benchmark too):

Undo stress test complete:
	15000 sentences inserted
	5000 undo chains of max length 9
	undo-limits: 50000000 strong: 80000000 outer: 100000000
	buffer-undo-list: length  261871,  23663505 bytes
Buffer has 16699 lines, 103312 words, and 736540 characters.
> Launching vundo: 
Elapsed time: 16.320850s (0.225208s in 2 GCs)
> Navigating to tree root: 
Elapsed time: 91.042314s (5.147266s in 42 GCs)
	buffer-undo-list: length  277581,  24490891 bytes

Here navigation starts to stutter (though basically still usable), and you get the dreaded "No possible route" message sometimes. Also assertions occasionally fail when navigating to a child branch:

Debugger entered--Lisp error: (cl-assertion-failed ((not (and (consp buffer-undo-list) (null (car buffer-undo-list)))) nil))
  cl--assertion-failed((not (and (consp buffer-undo-list) (null (car buffer-undo-list)))))
  vundo--move-to-node(#s(vundo-m :idx 4 :children ... :parent ... :prev-eqv nil :next-eqv ... :undo-list ... :point 10 :prev-saved-ts nil) #s(vundo-m :idx 14 :children ... :parent ... :prev-eqv nil :next-eqv nil :undo-list ... :point 12 :prev-saved-ts nil) #<buffer *vundo-stress-test*> [... ... ... ... ... ... ... ... ... ... ... ... ... ... ...])
  vundo-forward(1)
  funcall-interactively(vundo-forward 1)
  command-execute(vundo-forward)

Speed seems independent of whether vundo is native-compiled, which is good (time is spent outside vundo).

Originally posted by @jdtsmith in #66 (comment)

Idea: on-demand quick-drawing to mitigate long-line issues

As outlined in #68, when undo chains get quite lengthy (exacerbated by saving undo history), vundo and emacs in general slow down. It's highly sensitive to line length (M-: (current-column) tells you how long your lines are). Once past say 5k-10k columns, slowdown becomes apparent, and at >20k it becomes very noticeable, not just in vundo, but in all buffers while vundo is open. We increased the draw speed by ~10x in da90207, but that only affects how quickly vundo is drawn, not the slow-down from having such long lines around at all.

Here is an idea to mitigate this:

  • Rather than drawing into the buffer, "draw" into a data store — a ragged list of vectors.
  • When point moves (via normal vundo navigation), if this necessitates the display of new data in the window, redraw quickly from the data store: quick-draw.
  • Each vector in the data store list will correspond to one row of the display, and will be tagged with a column offset of its first entry, such that the full set of vectors completely describes what should be drawn at any given column offset.
  • The vectors contains a compact representation for the drawn item at point, entries like:
    • Type: blank, node or connector.
    • If node, the node object itself.
    • If connector, the connector type: horizontal, vertical, angle, or angle-branch.
  • "Drawing" to the data store is then straightforward:
    • use precisely the current draw algorithm, except instead of examining what's in the buffer, examine what's in the data store, extending it as needed.
    • This may need to use lists while building and be converted to vectors at the end of the draw. If all the list traversal during draw is too slow, using a temporary list of short vectors (e..g. length=512) and adding new ones to it as needed will cut this down.
  • With the data store fully populated, quick-drawing then just involves:
    • computing the (notional) column offset of the desired draw's start.
    • starting with the first vector on the list whose offset is exceeded by the desired final column offset (given the desired draw width), skipping to the appropriate offset within the vector.
    • translating the entries found there into drawn nodes (indicating saved status, adding the node as a text property, etc., as usual).
    • continuing to the next row-vector which contains in-range data and drawing those, until all row-vectors have been visited.
  • We could choose to draw just a window-width's worth of columns (so that moving off window always necessitates a quick-draw), or some larger number of columns (1000?) to minimize redraw; this would need some experimentation.

Simple diff functionality

I missed the "diff" function of undo-tree. You don't always need it, but for complex changes, it can be very helpful to figure out which branch you want. I cobbled together something that works for me — an "on demand" diff on key "d". I didn't study vundo's source closely enough to do this more intelligently, but it would be lovely if something like this made it into vundo.

Thanks for this simple, robust, and great tool.

(defun my/vundo-diff ()
    (interactive)
    (let* ((orig vundo--orig-buffer)
           (source (vundo--current-node vundo--prev-mod-list))
           (dest (vundo-m-parent source)))
      (if (or (not dest) (eq source dest))
          (message "vundo diff not available.")
	(let ((buf (make-temp-name (concat (buffer-name orig) "-vundo-diff"))))
          (vundo--move-to-node source dest orig vundo--prev-mod-list)
          (with-current-buffer (get-buffer-create buf)
	    (insert-buffer orig))
          (vundo--refresh-buffer orig (current-buffer) 'incremental)
          (vundo--move-to-node dest source orig vundo--prev-mod-list)
          (vundo--refresh-buffer orig (current-buffer) 'incremental)
          (diff-buffers buf orig)
          (kill-buffer buf)))))
(define-key vundo-mode-map "d" #'my/vundo-diff)

dealing with evil-mode

I saw this package adds itselft to evil-emacs-state-modes.
I understand that is for preventing problems with the bindings.
while evil-mode adds vim like functionality to emacs, it doesn't deal with most third party packages.
For that reason evil-collection was created to add that vim like funcionality.
I guess most people that use evil-mode either use evil-collection or are used to deal with those problems themselves.
There are different strategies to better integrate packages to the evil workflow.
Some users might set emacs-state for special modes, some might use evil's motion-state,
others might just locally override evil-normal-state bindings.
Vundo enables evil-emacs-state by default, this might interfere with strategies that evil-mode users already developed.
evil-collection-vundo.el is part of evil-collection. that file for example sets evil-state back to normal-state and rebinds the keys to use "hjkl" for motions in vundo-mode.
I assume most evil users would like to have those bindings, if they are not using evil-collection they are likely going to try to remap the keys and have to first realize, that vundo sets the state to emacs-state (it took me a while to find that issue).
For that reason, i recommend not setting any evil specific things to try to make some bindings work in evil-mode.
Those bindings are not evilish anyways.
Or at least make the state change optional and mention it inside the readme file.
What do you think?
Thanks

color problem when use vundo-prev or vundo-next?

Normal: I comment a paragraph, it changes code block's color, and I use undo, it uncomment back, color restore.

vundo: I comment a paragraph, it changes code block's color, and I use vundo-prev and vundo-confirm, it uncomment back, but color don't restore.

Does it relate to read-only mode ? I checkout e136164, it's OK.

vundo-next doesn't work for me?

This is an awesome package. I am attempting to use it with undo-fu, and it draws the tree fine; <back> and <forward> work as expected. However, I am unable to move off the main stem with undo-next.

image

I don't see any errors or messages, can you help/suggest some next steps to debug this?

Option to move cursor one character back?

Currently, the point is placed after the node glyph instead of before.

When using Emacs in some terminals, the cursor can't be hidden, and can't be restyled, especially not per-buffer (and doing it globally for the whole terminal usually makes for worse UX over-all).

One real-world example of where you basically have to use such a terminal is in on Android, in Termux. Technically that terminal supports some standard control sequences to restyle the cursor (neovim manages to do it), but Emacs doesn't seem to know how to do it? Anyway, for me this is a huge part of my daily Emacs usage.

So it's a super minor aesthetic issue, but it seems like some users would prefer it if a can't-be-hidden/changed box cursor displayed on the node rather than to its right.

vundo-stem-root fails when executed from a particular location

When running vundo-stem-root from a child of a branch besides the first, an assertion is raised.

Refreshing again (from calling vundo-next for example) shows the action is infact completed.

vundo_stem_root

I checked back to the early history of vundo and this appears to be a bug in the initial version (not a regression).

New release?

Hi @casouri, since vundo is not available on Melpa, I can't get the lastest changes without using something like quelpa, do you mind making a new release please? It's been a while.

Limit the size of the displayed history to speed up Vundo

When the history gets very long Vundo takes quite a bit of time to start up and then it is rather slow. I guess limiting the length of the history would solve this, but how can I limit the size of just the displayed history?

PS: I use

(setq undo-limit         (* 96 1024 1024)) ;  96 MiB. The change group at which this size is exceeded is the last one kept.
(setq undo-strong-limit (* 128 1024 1024)) ; 128 MiB. The change group at which this size is exceeded is discarded itself (along with all older change groups). There is one exception: the very latest change group is only discarded if it exceeds ‘undo-outer-limit’.
(setq undo-outer-limit (* 1024 1024 1024)) ;   1 GiB. If at garbage collection time the undo info for the current command exceeds this limit, Emacs discards the info and displays a warning. This is a last ditch limit to prevent memory overflow.

can it slow down Vundo?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.