Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Resolving the great undo-redo quandary (github.com/zaboople)
490 points by kerblang on Nov 11, 2022 | hide | past | favorite | 236 comments


> Second of all, there's no need to include "window shopping" in our recorded history; this is when you undo a ways, take a look around, then skedaddle out of there back to "the present" without changing anything. This is just a matter of moving information back and forth from undo-stack to redo-stack.

Usually the whole reason I might undo for a while is to get back part of something I was working on and merge it into other changes I’ve made since then. I’m curious why the author dismisses this, personally cherry picking old history is my primary workflow with undo. In editors that support the undo history they’re advocating here, like emacs, I’m usually undoing a long way so that I can put something in the clipboard, then redo all the way back and cut-and-paste the previous partial state.

It is nice in emacs that typing after undo doesn’t lose my undone edits, that is handy and is a nice little safety net, but it doesn’t actually solve the problem I normally want to solve.


The author isn't dismissing "window shopping". They're just dismissing the need to keep track of the "window shopping" in the history, since you're not changing anything in the history.

If you have state1, edit it and get state2, edit that and get state3, then go back to state2 and copy something and then go forward again to state3 to paste that something, why should the history from state3 (in reverse chronological order) then be [state2,state3,state2,state1] when it could just be [state2,state1]? This is what the author claims, and it makes sense to be. If I undo, don't make any changes, then redo, I don't want that undo-redo to then be part of the undo history.


This. To elaborate, the problem is that without the "window shopping" optimisation, you are forced to repeat all your "window shopping" excursions twice every time you want to go a little further back in time.

To illustrate how horrible this is, I'll continue the example: When you paste that something, you get state4. Suppose you then wanted to copy something from state1. Without the "window shopping" optimisation, you would have to go state4,state3,state2,state3,state2,state1, copy, state2,state3,state2,state3,state4, paste.

If you then wanted to copy something from state0, it would take 34 steps!


I might be understanding you poorly here...

The author is not dismissing this in the sense of saying “it's impossible and that's okay” but rather “it's possible and it never generated the ambiguity in the first place, so it needs no special logic.”

So you probably actually use undo-redo in two big ways:

1. I just pasted the wrong thing or modified the wrong file or the kid grabbed the keyboard etc, I want to quickly erase N changes.

2. I want to grab info from the past and surface it back to the present. I cut something and then lost my clipboard contents, or so. This is what you described.

The problem in the article is, basically imagine that you get the key wrong. You do your browsing thing, you are N levels back in time, but instead of Cmd-c to copy this block of code, you accidentally either Cmd-x which deletes it, or Cmd-v or Opt-c or so which overwrites it. You were trying to do (2) but now the dominant heuristic suggests to the computer that you are actually doing (1). Because in the usual implementation of undo-redo, undos push changes off the history onto a stack, redos pop changes off the stack onto the history... And any new change clears the redo stack! That stack clear is the heuristic providing the difference between (1) and (2). When the heuristic misfires, it misfires in really dangerous ways, potentially eating hours of work.

So (2) is not being dismissed as “who cares” but rather as “that's fine because our heuristic has not yet led us astray, every implementation of undo that has redo will solve obvious-(2) and obvious-(1) correctly.”

This is where I might be misunderstanding you, I am not clear if you are saying that your editor is cooler than this default interpretation in some way that does not require “redo all the way back and cut-and-paste the previous partial state”...? If so then I would have to hear more before I could know if the article’s “history is always chronological, but browsing history is different than undoing it: undoing it only happens on an edit to an old browsed copy and saves a history-rollback into the modern chronological history, rather than fussing with undo graphs or whatever” approach works that way.

Edit: actually, the article’s approach of having a browse mode solves your problem automatically in a counterintuitive way... Cut instead of copy, you will get launched out of browse-mode, then undo twice to get to the tip of your history. Counterintuitive but definitely saves some keystrokes.


Fundamentally, the example I’m bringing up requires a tree structure conceptually, mixing some older history with some newer history. The solution in the article doesn’t address this, and it seems to dismiss the need for this use case, but I’m saying the whole reason I get into a situation where knowing how undo and redo work, and when I might care about whether typing after redo will lose history, is because I’m trying to merge old state with recent edits. I want both changes, not just one or the other.


Have to disagree here. "History mix" is a super common use case for me, as is a "clip slip" scenario - where I fumble and erase the future.

The technique as described works fine for this. I use it in the terminal all the time (an interface where state is saved on execution rather than keystroke). You go up through the history, copy the part of the command you need and head back to the present to use it.

I think it just comes down to the point at which state is modified. I wouldn't expect an implementation to modify state just by viewing the history, only by editing it. Suffice it to say your use case is fine.

Edit: tho, reading some of your other comments I agree that a better interface for this use case (e.g. a "revert to last meaningful edit" action) would be useful. Tho its slightly tangential to the discussion.


It doesn’t sound like you have a problem though. Just keep doing what you’re doing.


Hehe, sure, but there are better ways to do this, and it’s a common need. If the best thing we have is hitting undo 45 times to find something, copy it, try not to accidentally type anything and try not to accidentally lose the clipboard, then redo 45 times, then do a bunch of manual editing… now repeat the entire process for every file, when there are several involved… if that’s sufficient, then SVN is sufficient for source control, and we have no need for Git, right? ;)

My real point is that the “Great Undo-Redo Quandary” is broader than what’s in the article, and the solution discussed is incomplete, doesn’t solve a big chunk of the reason people are using undo multiple times.


In the proposed article, it sounds like if you accidentally typed anything you would have to hit "undo" twice instead of "redo" N times to get back. Still seems confusing.


But if you accidentally type something now you can't just hit redo N times to get back. It's gone.


Not with this new algorithm.


"And everything goes back to the beginning"


Is it though? If you really just needed part of what you had and hour ago, then there's nothing wrong with scrolling back to an hour ago, copying that one bit you needed, scrolling back some more to where you were a minute ago, and integrate it as desired?

(which, note, is not window shopping: you didn't go back to have a look and skedaddle; the past is a data store, and you're accessing that data store for productivity reasons)


I feel like it is, so I’m not sure what you’re asking. It’s sometimes error prone depending on editor, and sometimes a lengthy and manual hassle to undo many times & copy old state, and then to merge if it’s not a single contiguous block and not a single file. It’s way easier to do what I’m talking about if I’ve committed changes in git every 5 minutes, but I don’t always do that in advance (most people don’t), and therefore could be nice to have a non-linear undo UI that addresses the workflow example I’m talking about, which happens to be a common reason that people even think about what happens in multiple undo-redo scenarios in the first place. What’s wrong with pointing out that 1- we can make better tools if we want, and 2- linear undo/redo doesn’t solve the whole problem?


Doesn't sound like linear undo/redo is the problem, but rather ability to navigate it. Timestamp the actions and you could offer "undo to 1h ago". Or you could allow searching the undo history.

But I may be having a hard time understanding why you need this as your description just does not fit my typical workflow.


Many times I want to undo to a previous state, but then apply again a few of the changes I just reverted, basically cherry-picking from the future (from the redo stack).

It would be interesting to be able to see all edits as individual patches/diffs that you can commit/discard individually. Like a mini automatic git inside the editor, automatic in the sense that every "edit" creates a commit, undo/redo move HEAD.

This would be entirely optional, you can continue using Undo/Redo as usual, but if you need to do a more complex history operation you can open this "git" view and operate on the edit tree directly.

Photoshop has this nice undo panel where you can see individual edits and click your way to the desired point in history:

https://www.bwillcreative.com/wp-content/uploads/2020/08/how...

One can imagine such a view in a text editor, where each entry shows a diff of what that step did.


Use local history in any JetBrains IDE.


I am not a JetBrains user, but it looks like that only applies to saved versions? Undo normally holds every typed change. https://www.jetbrains.com/help/idea/local-history.html#resto...


You’re usually autosaving. So it’s not every character, but maybe every minute and lots of other logical triggers like running the file. I’ve found it works very well.


In Emacs, if you select a region of text and invoke undo, it applies only to the selected text. Very useful sometimes.


(But also buggy...)


Seriously. This is one of those features I forget other people don't have access to. Anything less is just uninspired.


VS Code has this feature too, it's just not known.


And with the diff panel you can easily cherry pick selected changes to apply.


This is what I thought the undo redo quandary would be. The fact that you can undo an undo seems like tablestakes to me.


In Atlassian Sourcetree you can stage the full current file, undo live changes in your editor, make different changes to your live document, and then reapply chunks of your prior changes from staging to live document (actually looks like undoing unstaged chunks).


If the changes "conflict" it might be best to use a "smart" editor that can somewhat figure out changes on a higher level.


Agreed.

When I write "foobar", delete the last three characters (so that now I have "foo", then I write a "t", I would like to be able to undo the previous deletion (giving me "footbar"), not just adding the last character.

Seems to me that would work especially well in editors that have the concept of verbs + objects, like vim's "delete until the end of the line" or "replace current word with...", but maybe it would work with all kinds of editors?


Should it be “footbar”? Or “foobart”? I could see a case being made for either.


  foo<<<bar===t>>>
I’m kidding, but only a bit: the (in hindsight, reasonable) lesson of the Pijul paper[1] is that if you want to do the most general merge and (therefore) avoid any arbitrary choices, you’re forced to extend your model from consistent files as sequences to files potentially containing conflicts as DAGs (or something even more complicated if you have data more complex than a single flat sequence or edits other than insertions). Or you can very carefully make a consistent set of arbitrary choices, like implementations of operational transformation usually do.

[1] https://doi.org/10.1016/j.entcs.2013.09.018


Depends on where the cursor is when you press redo, because it was originally inserted at the cursor position.


I would definitely not want the behaviour of undo/redo to depend on the current position of the cursor; that’s what paste is for.


Vim has this, it's :earlier and :later

https://vimtricks.com/p/vimtrick-time-travel-in-vim/

    :earlier 3 – Undo the last 3 changes
    :earlier 5m – Go back to the state of the file 5 minutes ago
    :later 2 – After undoing something, redo the next 2 changes
    :later 1h – Travel forward through the change history 1 hour
Vim also stores the tree of changes, but it's a pain to access without plugins.


What a great feature!

Is the author of that feature here?

If so, I want to know how they could develop such a feature and be so quiet about it.

It's like someone baking a birthday cake and then dropping it down an abandoned well.

Just imagine how much more productive we'd all be if Vim bros were as loud as the crypto bros over the past decade!


You just need to get vim bros going. Just go "omg I hate vi so much" at a tech meetup and sit back for the fire hose of vim features to start.


It's like Cunningham's Law but with vim commands.



And in that 16 years, where was the Vim-sponsored nascar with ":earlier 39" printed on the hood in Courier New?

Shameful.

(I'm actually afraid to continue making jokes on the non-zero probability that someone here reads this and tries to start a Vimcoin...)


Another great Vim feature is persistent undo. You open a file, make modifications, save and close. You open again and the undo history is still there. You'll just have to enable that feature.. don't think it's on by default, unfortunately. Find a ~/.cache directory to store the undo history in.


This would make my life so much easier — constantly accidentally closing a file when I want to hold on to the undo history to redo something. Do you know the name of the feature/flag/setting?


This is what I have in my ~/.vimrc:

    " https://vi.stackexchange.com/questions/6/how-can-i-use-the-undofile
    if !isdirectory($HOME."/.vim")
        call mkdir($HOME."/.vim", "", 0770)
    endif
    if !isdirectory($HOME."/.vim/undo-dir")
        call mkdir($HOME."/.vim/undo-dir", "", 0700)
    endif
    set undodir=~/.vim/undo-dir
    set undofile


That's neat, and a missing part of my vimrc - making sure the directories I use exist. Since I use .cache and exclude those from backups, it's actually bit me.

I've added this thanks to you:

   call mkdir($HOME . "/.cache/vim/", "p", 0o700)
I think this avoids using the conditionals, by just using "p".


Using strictly vim for years, I'm still mind blown by the sheer amount of "don't knows" within the tool that I miss like this.

Thank you!


I think what vim has is different and better than what OP describes - it's two separate change logs, one for undo/redo and one for time-based jumping. Trying to fit both of those constructs into one list gets messy.


Came here to look for comments about VIM's `g-` and `g+` feature, was not disappointed :)


Are those the shortcuts for it? I can't believe I didn't realise vim had this.


Wait until you see what intellij can do. It can do that, while also layering it in a logical, graphical diff viewer


Vim has plugins for that. ;)

I use https://github.com/mbbill/undotree but if that's not to your taste there are many others.

e.g. https://docs.stevelosh.com/gundo.vim/


F** right off. How have I used Vim for fifteen years and not learned about this.


Haha, that made me spit my drink. :D


I rarely have to use it, but Undotree is so amazing when you need it.


I don’t doubt that IntelliJ overall is more user-friendly. But just to note, Emacs’ undo-tree can also show the diff for each change.

Edit: Screenshot (with an unusually blue Emacs theme): https://www.emacswiki.org/emacs/UndoTree#h5o-5



Visual Studio Code offers something similar in the timeline view. It's not as fine-grained, but still can be a life saver.


I have started to learn Vim about a year ago and still learning new tricks each day


Wait until you say this after 10 years.


After 10 years he might even be as fast as with a non-modal editor.


Every non-modal editor I’ve used is significantly slower at editing text than Vim


We have been through this argument many times. Here's Tog at Apple in 1989:

https://www.asktog.com/TOI/toi06KeyboardVMouse1.html

And even then, it was old news. There was behavioural research in the 70s, the same kind that was used for designing cockpits (and which saved lives), showing that modal interfaces were undesirable.

It can well be that you're a god with vim. But odds are, then, that you could have been a god with modern CUA-derived interfaces too (or with Emacs) if you'd put similar effort into it.


Supposedly this article has been mis-cited of late and the keyboard shortcuts / mouse shortcuts of yore are distinct from what we have today. I can't find recent links but I'll offer the general advice that words used in the 70s do not necessarily have the same connotations they carry today.

EDIT: here's an HN discussion https://news.ycombinator.com/item?id=28067729


Did you misspell "editor with language intelligence?"

Because yeah, vim won't beat IntelliJ, but you can get it to be pretty decent with LSP:

https://github.com/prabirshrestha/vim-lsp


I use vim exclusively, and I laughed, because you are sort of right.


I’m at 15 soon.


25, with 5 years of other vi editors previous to that. Vim is pretty great.


20 and I only discovered :vsplit/:hsplit this year.


Feels like I'm missing something...

If I press ctrl-z ctrl-z, I expect the last 2 things typed to be undone. Based on what he's saying, the first ctrl-z undoes one step, and the second ctrl-z undoes the undo i.e. puts me back where I started, with no way to get back further.

Is there a special case for multiple undo's in a row? If so it seems unclear where to draw the line. If not it sounds nonfunctional.


In Emacs, undo makes an undo pointer go down in the undo stack. Pressing undo again goes back another step. If you do any other regular edit, the pointer starts over at the top of the undo stack. Undo puts its own edits on top of the stack like any other command.

So if you "undo, undo" you undo two things. If you "undo, edit, undo", you're keeping the first undo but reverse the edit. If you "undo, edit, undo, undo", you're back to where you started (except your undo stack has now grown).


Yeah for something that is supposedly intuitive this is the worst explanation I have ever read. I think what he's saying is that:

It works exactly like normal undo/redo, but if you make an edit that would normally wipe your "redo stack", then instead that redo stack is moved into the undo stack.

So:

  Type "hello"
  Type "world"
  Type "!"
  Undo
  Undo
  (Editor is "hello", with "world" and "!" on the redo stack)
  Type "dave"
  (This would normally wipe the redo stack but instead it moves it to the undo stack)
  Undo
  (Editor is "hello")
  Undo
  (Editor is "helloworld")
  Undo
  (Editor is "helloworld!")
  Undo
  (Editor is "helloworld")
  Undo
  (Editor is "hello")
I like the idea. It's a little weird that typing changes the undo stack but it already can change the redo stack so I don't think it matters.

It needs an understandable explanation and ideally a web demo to gain traction though.


I think the best strategy to make it intuitive would be to flatten the redo stack as a single command, so that the pile of successive 'undo's gets redone as a single step. In your example:

  Type "hello"
  Type "world"
  Type "!"
  Undo
  Undo
  (Editor is "hello", with "world!" on the redo stack)

  Type "dave"
  (Editor is "hellodave")
  (This would normally wipe the redo stack but instead it moves it to the undo stack)

  Undo
  (Editor is "hello")
  Undo
  (Editor is "helloworld!")
  Undo
  (Editor is "helloworld")
  Undo
  (Editor is "hello")


I get what you are saying, but I think it is confusing to refer to a 'redo' stack at all. There is only ever one stack, and you are always at the end of it. To go back through time you copy the states onto the stack (stack isn't even the right word, I guess but I'll stick with it)

Consider typing the alphabet

Stack:

  1.a
  2.ab
  3.abc
  4.abcd
Now you 'undo' back to place 2 and type 'x'. But while you are conceptually travelling back in time, what the stack really looks like now is:

  1.a
  2.ab
  3.abc
  4.abcd
  5.abc
  6.ab
  7.abx
A conventional undo stack would leave you stuck at a new place 3: abx, all redos gone. Undos from that point can only take you back to place 1 or 0. (Just tested this out on OS X TextEdit, it does this). With the linear stack, there is no redo, only undo.

I think that is what the article means at least...


Photoshop has nonlinear history. When this option is tunred on, you can undo several times, make a change, and not lose all the states you've undid - isn't this what he's talking about?


Yeah that's the same thing and is probably how you'd actually implement it, but conceptually you can think of there being an undo and redo stack.


Yeah maybe, I'd have to play around with both to see which is less annoying.


> If so it seems unclear where to draw the line. If not it sounds nonfunctional.

There is no line. It is just one long linear list. An undo operation is an edit to the text after all. So the edit made by the undo operation also goes as a text edit operation in the undo list.

This is the way undo has always been in Emacs. I wouldn't say it is nonfunctional. I use this in Emacs undo, redo (which is undo-of-undo) and it feels okay most of the time. It is good to know that Emacs will never lose any edit even if I have performed a confusing series of undos and redos. But it can get confusing pretty soon if we are undoing and redoing on the same edit too many times.

That is why many people don't like linear undo history and install an undo-tree plugin which makes undos easy to navigate in a tree. Yet another reason to design some computer/software history lessons so that devs can learn from the existing techniques, their adoption, benefits and complaints.


The way this approach is implemented in Emacs is that it depends on whether your previous action was an undo.

    A: A
    A B: AB
    A B C: ABC
    A B C undo: AB
    A B C undo undo: A
    A B C undo undo D: AD
    A B C undo undo D undo: A
    A B C undo undo D undo undo: AB


Interesting. So basically the last chain of undo doesn't enter the history until you do something else.

I often use undo as a faster delete when making edits, so I think I would still be annoyed by this (why am I seeing this crap I undid reappearing?) Now after a long undo chain I'm afraid to type for a new reason: it will pollute my undo stack. But it might be reasonable if my brain was used to it. Can't knock something I've never tried.


Note that if you delete something and press Undo, it restores the content that was there before removing it.

The "branching undo" in the article extends the concept to being able to recover things that were at some point in your "redo" pile, but which in other editors would been have lost when you typed something else while in that state.

As the article explains, the simplest mental model is "rewind in time to the point I was 5 minutes, 10, 15, 20 minutes in the past". If you've been undoing and redoing things, going through that point in the past will retrieve those removals and retrievals.


How is undo a faster delete?


Like the GP, I also use undo as a faster delete. The key is that undo in Emacs doesn't just go one keystroke at a time. By default it will basically quash a whole chunk of similar operations and undo/redo them together:

> amalgamating-undo-limit is a variable defined in ‘simple.el’.

> Its value is 20

> The maximum number of changes to possibly amalgamate when undoing changes. The ‘undo’ command will normally consider "similar" changes (like inserting characters) to be part of the same change. This is called "amalgamating" the changes. This variable says what the maximum number of changes considered is when amalgamating. A value of 1 means that nothing is amalgamated.

There's also a hidden 10s idle timer that will insert a boundary to break up the amalgamation. So if you type, pause and think, and then start typing again, the chunked undo/redo will always stop at the point that you started typing again. Taken together, it's usually quite effective at guessing how much to delete.


If you're only adding content, undo removes the most recent content inserted. But that's a very limited case of everything that undo can achieve.


The article says:

The act of undoing need only become part of our linear history if we squash the proverbial butterfly and thus alter "the past"; in that case the act of undoing itself is instantly recorded as a series of changes, by replaying the redo stack onto the undo stack twice: 1) Forwards, for the original changes 2) and then backwards, to record the history of our undoing.

By squash the proverbial butterfly and thus alter "the past" they mean make an edit after undoing.


No, Ctrl-Z Ctrl-Z still undoes the last two edits.

Basically Ctrl-Z is now "rewind" and Ctrl-Y is now "fast forward". Instead of a stack you have an append-only list. Even Ctrl-Z appends to the list. (I'm eliding the optimizations he mentions.) So if you Ctrl-Z then edit, you can still get back to the state before your Ctrl-Z.


True, at what point do you include the undo itself in the undo history or do you skip it entirely?

Never considered that.


I feel things work most intuitively if the undo/redo functions simply navigate the history, but do not themselves form part of the history.


That's how every other undo works. The whole GRUQ is caused by undos not themselves forming a part of history...


That's it. You can think of an undo operation U at time (t) as a new simple command that rolls back the content to a previous version (t-x). If you undo that U operation, it's a new command U' that restores content again to version (t).

The history sequence would then be:

(t) --U-> (t-x) --U'-> (t)

And if you add (A) some new content (c) when in state (t-x), it becomes command , which can itself be undone with commands U'' (to remove A), U''' (to remove U):

(t) --U-> (t-x) --A-> (t+c) --U''--> (t-x) --U'''--> (t)


>every other undo

Ahhhhh, some time you should try a little app called Adobe Photoshop.


That means silently killing off potentially large amounts of editing history every time you edit after an undo. That's the problem this article is about.


I was thinking maybe you ignore it for the first 10 seconds or even better until the cursor changes to a new position, thqt way quick rewinds work in the moment. But later on you can restore the state with undos factored in.


This is exactly how undo works in Emacs out of the box. Personally, I prefer to install the undo-tree package and manually browse through the tree of undo paths. I'm not sure why this author finds a tree unacceptable for this purpose.


Additionally, emacs will filter undo/redo’s to changes within a region. If you select a region (no new command to learn) and use undo/redo, it will only perform those that affect text completely within the region. This is a superpower that delights me each time use it.


I did not know this, despite having used emacs for > 20 years - thank you!


Do you know a way to keep the region selected after undoing so as to continually undo within the region?


That’s the default behavior; perhaps you have customized something that changed it.


That region based undo is fantastic and has saved my bacon so many times! I really wish more apps had it, though I shudder to think of the implementation complexity.


Yes! Having regional undo is super useful sometimes. I switched from emacs to vim a long time ago, but still miss this feature (although not enough to go searching to find an equivalent vim plug in I guess). Lazy web?


Well if you want to return to Emacs, I can attest as a former long-time vim user that the emulation layers for vim modes in Emacs are fantastic! They're really the only good part of vim, as the configuration language was always a bit less desirable than elisp, imo.


I've tried in past but always found the vim modes to be a bit lacking. My last attempt was probably 6 or 7 years ago though so maybe worth another shot!


There is a very opinionated and batteries-included "Vim-y" Emacs called Doom [0]. A decent way to approach this if you don't want to start writing elisp outta the gate to have fancy editor features is to learn enough of the Doom config to tweak it to your liking, then add some elisp customization in as needed. I personally use this approach, usually cribbing config and elisp tweaks from the top contributors' configs.

The Evil layer that Doom and most everyone seems to use for Vim modes works really well, and has a lot of ways to tweak things (e.g. changing `j` to `gj` for going through line breaks in normal mode; I forgot what that setting is called...).

There is something to be said by bootstrapping your config entirely from scratch instead of using a "config framework" like Doom, but that can be too daunting and end up preventing one from trying things out.

[0] https://github.com/doomemacs/doomemacs


I ported my vim configuration to Evil Mode seven years ago and haven’t looked back. Evil Mode is the best done vi emulation layer I’ve seen and I think it even improves on vim in at least one area (the behavior of `I` in visual line mode).


Evil mode is the modern standard I believe. Not a user myself, but I hear good things.


I switched from Emacs to IDEA because IDE features are frankly unrivalled but I still think Emacs does the text editing part plainly better


Because it requires more than two commands


Yeah, I spent a long while (years) using pretty much exactly the solution he describes, as implemented in emacs. It's not as good as a tree, because undo/redo cycles make it very annoying to traverse through the history. I greatly prefer undo-tree.


And it is exactly why I find Emacs undo so hard to work with. But maybe I will get it now I've read this article.


You can, if you like, install the 'vundo' package, which gives you a tree-based overview of your undo history, making it easy to switch between branches and go backward and forward. It's basically like the undo-tree view for the default undo mechanism.


Note that Emacs undo is not tree based, but works as described in the article. The tree based undo is a separate package one has to install.


As I wrote.


Two suggestions that might make it easier for you. There's a package called `undo-fu` that puts a thin layer on top of the undo system to keep the behavior strictly linear.

And built in, there are the `undo-only` and `undo-redo` commands. Unlike the base `undo` they will only walk in one direction.


See `:h undo-persistance` for that feature. In particular you want to do `set undofile` in your vim config


I'm not sure how you deduced that I'm actually a vi user, but it's interesting to see that vim has similar support but using separate linear undo and redo commands g- and g+ . undo-persistance and undofile seem to be orthogonal to that though, I'm even less sure why you're bringing that up.


Sounds like this solution would best be implemented with an undo-tree, but also a linear history mapping with pointers to that tree - the linear history would just log movement within the tree over time e.g. undo-redo would just be a backwards-forwards movement.


That's exactly what you get with undo-tree. The regular undo/redo commands continue working as normal, but you can also manually browse the tree if things get too nonlinear for you.


The one I saw had you browsing the tree, rather than a linear history representation of a tree.


Make sure to check the value of `undo-limit`, as the low default value greatly (and needlessly, on modern machines) nerfs undo. It also applies to extensions like undo-tree and vundo I believe.

> undo-limit is a variable defined in ‘C source code’.

> Its value is 10000000

> Original value was 160000

Speaking of extensions, I find undo-tree pretty buggy. I might be one of a dozen people who actually love the default undo/redo mechanism.


He literally says why at the top of the post. People find it too complicated.


No, he says most people do not actually implement the interface to make it make sense. So they have to make ctrl-z and ctrl-shift-z do the logical linear thing which requires all sorts of craziness which is what is complicated to do correctly (e.g. without breaking user expectations).


I confess I didn't get that the post was about emacs. Reading the intro, it sounds more like it assumes nobody ever implemented something. And then goes on to describe what sounds a lot like how emacs works.


That’s the impression I got as well, since it never mentions Emacs at all and then describes exactly how Emacs works.


No they don’t. The method they are praising is already the base implementation for emacs.


I implemented something similar for a interactive whiteboard project once. The undo operation would simply be appended as another action that itself could be undone by re-doing it.

However, as you played with it, it became confusing because it ran counter to ingrained expected behavior of an undo/redo dual stack implementation. Eventually I implemented the classic approach.


There's an interesting parallel here: the system proposed in the post is concatenative, and the undo tree style is, well, a tree. Sort of a Forth v. Lisp thing.

Here the big disadvantage of Forth, the implicit stack state, is less obviously balanced by something good. The UX of undo trees could certainly use improvement, it would be great if undo/redo behaved as expected, but any pivot point would automatically pop up the tree view. Then there's cherry-picking, but I have a solution to that.

This is in fact the thing I want most from an editor: select a region and then undo/redo changes only in that region. Search and replace tends to work this way, but I've never seen it in an undo/redo system.

How many times have you modified a function, broke it accidentally, and just didn't notice and made more changes? So the tests go red, you run a diff, and you see exactly what needs fixing: but you copy and paste out of the diff, because you can't just undo within the offending function.


The !Edit text editor in RISC OS used this algorithm in the late 80s. https://www.databasesandlife.com/risc-os-edit-undo/

I must admit I found it a bit confusing at first. Took me quite a while to form a mental model of how it worked.


As another comment mentioned, this is exactly how Emacs handles undo, so this is not revolutionary.

Two important things to point out about this approach:

1. you will never lose any previous information (barring memory limitations). You can be confident any previous state can be retrieved somewhere along the linearized history, and you don't need to search along a potentially thousand branched tree. Just follow the linear history and you will find it.

2. this tracks the chronological state of the buffer. For some reason, people do seem to get confused with Emacs's undo functionality because they're familiar with the "Microsoft Word" style undo, but I feel like it's intuitive if you think of it as a literal timeline of the buffer state. If you want the state of the buffer five minutes ago, you will need to undo past the one minute ago state, the two minutes ago state, etc. And then two minutes later, you can undo two minutes back to get the same state again even though the "original" state is now seven minutes ago, because when you "undid" you brought the buffer back to that state in the present.


Semi-related: What I really wish all my editors and IDEs would have is some kind of "delete history" view into my deleted text (especially blocks of text).

I'll often delete something, then work for a while, then realize I need that thing back again so I have to VERY carefully undo everything after the delete until I get to a point in history before it, copy the deleted text, then re-do everything back to my original state while making sure not to accidentally execute any inputs which would burn down the redo stack.


Just a list of tools that have this, in case you use any of them and didn’t know: Vim - has this with undo tree Emacs - has this as well Jetbrains - IDE’s have this with local history they show changes made. VScode has this I’m pretty sure by default otherwise there’s a plug-in for it since I remember doing it. Also just want to add something really cool about vim and undo/redos that relates to this post. With vim your able to “block” or “bunch” your changes which allows you to control what an undo will do. For example, going into insert mode and typing a sentence than exiting insert mode would be one “bunch” and undo would undo that entire sentence. If you wanted to you can control it by leaving insert mode after a adding a single word or making a single edit and that will be what the undo will revert. Probably rambling a bit here, but the concept of controlling what will be undone/redone is a really neat feature of vim.


Jetbrains IDEs can do this using the local history feature. Browse through time on a separate tab, copy what you need, and paste it back into the present.


Whats even more amazing is that you can right click the package/folder and get local history and see entire files that you previously deleted.

Took me a while to discover this but has saved me almost as many times as local history in a file. Also makes me feel much better when refactoring and deleting tons of old code.


A lot of IDEs (I know Eclipse and VSCode both do this) will create a copy of the file you're working on each time you hit save. You can then navigate back through the local history of the file.

I'm conditioned to hit Ctrl+S with almost every change, so this gives me a very detailed history of my revisions.


You can use “cut” instead of “delete” and clipboard with history tracking, for example Alfred on macOS.

Then you cut, do some changes, and later on realize you wanted something from the cut code, bring out the clipboard history and fish out what you need.


Emacs does this by default. Anything you delete ends up in the "kill ring", and you can cycle through that when pasting ("yanking") something. Packages like Consult[1] provide version of the yank-pop command that, instead of cycling through the kill ring, make it searchable.

[1]: https://github.com/minad/consult


Yes, but having thousands of snippets in a bucket can be quite confusing too, where do the pieces go back and so on.

For this reason I comment out code then keep it until I’m sure I no longer need it. This works in sessions, and do the final delete, clean up etc at the end of the session before committing the changes to the repo. I see devs keeping commented out code in repos but that adds too much noise, makes the code a real mess..


Maccy is a great free and open source clipboard history tool with a nice interface (Alfred requires the power pack purchase for this feature)


Oh cool-- are you saying Alfred will let me override the delete key in the case of highlighted text so it always executes a cut instead?


I think they’re just referring to a clipboard history offered by Alfred, not remapping the delete key


Eclipse has “local history” for each file save operation.

You can even do a graphical diff against each candidate timestamp in a gui wizard

As others have mentioned JetBrains cloned this feature too


Vim stores all deleted text automatically, up to a max of 10, I think.

Probably very possible to extend that with some advanced vimscripting.


In this thread: people discussing all the excellent features they wish their text editor had that emacs (and/or vim) has had for literally decades.


And/or JetBrains.

Makes me wonder honestly what tool in widespread use doesn’t have this? Notepad?


Vim supports practically unlimited undo with branching history. You can visualize it and hop to any undo with a plugin.

https://github.com/simnalamburt/vim-mundo


Yes, but GP wanted something that would just keep all deleted text.

Sort of, anytime you delete something, that text gets stored somewhere in case, 2 hours later, you realised you wanted it.


Yes, vim’s “set undofile” will create a persistent undo file that stores all changes to the file indefinitely. You can undo changes made months ago over reboots. If you move the undo file between machines, you can undo on different computers.

Undoing a change from 2 hours ago in the same editing session is the trivial use case for undofile.


But undoing is not what the original commenter wanted.


When set undofile is combined with :earlier and :later being used in place of undo and redo I think vim behaves as the author describes.


My biggest gripe with undo is in cases where it's unclear what undo will do if anything, and the action is file or email related without a clean redo option.

Thunderbird - oops I just keymashed and did a bunch of random shortcuts. OK great I can Undo infinitely with no indication of what emails are being moved or undeleted, etc.

Windows Explorer, Dolphin etc - undo can be pretty opaque unless you already know what it is that is being undone. And there isn't a redo!

> Of course that tree requires a navigation system for users to pick their way back through the undo-redo history, leading to all sorts of complicated user interfacery that nobody has time to deal with.

I would LOVE the complicated user interfacery to be in all applications to show at least the list of actions! Image editors are great at this.


Unbelievably, Solidworks does not even have undo for some things and undo/redo are terribly broken.


The best form of history, undo and redo I've used is on hte various Jetbrains editors. It's a feature called Local History and is insanely useful.

1. It's always on. It never requires an explicit save or commit;

2. It automatically saves any changes and timestamps them;

3. You can view the state of a file or a tree at any moment in time and then pull out that file (or tree).

4. If you do that, it becomes part of the temporal history so you can always go back.

I find this completely natural because usually when it's necessary you think "Oh I need to unwind most of what I did in the last hour" or "I changed something I shouldn't have yesterday". You're not thinking about a line (or a tree) of changes. You're thinking about times and possibly ordering ("I changed X right before I changed Y").


> "Oh I need to unwind most of what I did in the last hour" or "I changed something I shouldn't have yesterday"

Vim's :earlier and :later can take a time parameter (i.e., 30m, 2h, 1d) which does exactly that.


Local History is hidden in the File Menu and does not have a shortcut. It is one of the most useful features in the Jetbrains editors, so I set Alt+Z as a keyboard shortcut (because Ctrl+Z is undo), which after a few years of using it still seems like a good idea.

One point that is missing from your description is that it always shows the previous state in the diff view, which also feels really natural to use (I only sometimes whish the diff markers were more distinguishable from the inspection and ToDo markers).


Yep, I use Apple Time Machine for much the same purpose. Once you've set it up, it's always on, you can just go back in time (the granularity is once per hour, but that's been enough every time I've needed to use it) and restore (or make a copy of) any file or folder. Saved me quite a few times when Git has let me down.


VS Code has that feature too, but only for individual files.


I wish VS Code would expand on that feature, coming from IntelliJ. It's really annoying that you need to type the name of the file you want to see the local history of, even when it's the currently active file in the editor. There should be a sidebar widget for it that's active for whatever the current file is.

The cool thing with IntelliJ is if you accidentally deleted a file you can just touch a blank version of the file and the history comes back. Not sure if VS Code does that yet.


There is a sidebar widget. But it starts merged in the File Explorer view, and you need to drag it over from there to the sidebar to make it independent - https://stackoverflow.com/a/71522634

The widget is called "Timeline".

> The cool thing with IntelliJ is if you accidentally deleted a file you can just touch a blank version of the file and the history comes back. Not sure if VS Code does that yet.

VS Code does keep the history after file deletion, but I'm not sure how to access it from inside.

I had to manually rummage through the storage directory to find a file I accidentally deleted, but it was an file which was never saved (think Untitled), so it didn't had a path that I could try creating a blank file at.


Fantastic! Thank you so much.

I think it's a fairly common situation that people using programming tools (VS Code, git, vim etc) can go for years without realizing there is a built-in feature that can solve exactly a problem they always struggled to solve. I'm not sure how to surface that sort of functionality in the tool, but I'm glad HN is here to point it out. One similarly cool thing I learned in a recent comment is that if you press dot (.) on Github web interface, it will launch an in-browser VS Code instance to edit the file you are viewing. Nifty!


It helps to read the VS Code monthly update release notes, it takes about 3 min, but you'll be aware of all new features they introduce.


You are a faster reader than me. The October update has around 4,700 words plus plenty of images to digest. I understand you likely mean to review the highlights, but a non-trivial amount of information is in each months update. And if you haven't been keeping up for the past 6 years, it isn't easy to catch up now. Not to mention the breadth of the marketplace.


Or you can just look at the history of the folder.


Wow, the article nailed the UX.

On the implementation side, I think the article implies that you might need to model the edits by Operation, rather than State? Otherwise, the cost of the undo scales O(N) of the content size, which is not viable.

It works well for Git, which models content by State, using Structural Sharing. But Git has built-in delta compression so the memory footprint is roughly O(1) per operation. I don't think implementing this on the web for a <textarea>, or [contenteditable="true"] is a "minimum" effort.

Challenges I can think of:

1. DOM api exposes state, not operations. You need manual diffing

2. When applying the operations, you need to manually restore cursor positions

3. CJK language IME might inject unwanted intermediate states that must be ignored


Google's internal source repository client CitC (client in the cloud) behaves this way for your entire workspace, forever.

It was so freeing - every single file save, delete, move and so on was saved without user intervention.

Realize that you were barking up the wrong tree for 2 days? Simply go into .snapshots and the entire workspace from before would be there.

This interface composed so well with other tools you could do a parallel bisection test across every prior state of your workspace and find the minute where you made an edit that broke some functionality. All on the cloud.

I miss this tool very much. No amount of git commit discipline can emulate it.


I'm surprised that tools like this aren't more common (even in non-commercial settings) given the tiny amount of space that code/text takes to store.


My way is a a ugly undo - copy - redo - paste.

Not efficient, sure, but I'm used to it and it solves my problem most of the time


Honestly IMO it's a bit better--often times I find myself wanting neither _this_ nor _that_, but some of each. I get to a point in a function where I realize changes I made elsewhere are no longer needed, but I want to keep my function the same. So I usually just copy the function (and paste it someplace so I don't for real lose it), undo everything, then just splice the function back in how I want it.


The main issue with this I find is a stray keystroke before you redo wiping your redo stack .


I solve this by making a git commit before diving into the undo.


This sounds very similar to Emacs' undo history, except Emacs doesn't implement the "window shopping" optimisation. Instead, Emacs has a command `undo-only` which is similar to `undo` except it will skip over undo-redo chains.

> Thus you can use up all available memory in approx. 64 quick steps by: ...

I disagree. If the editor uses a very basic implementation, then "Undo to beginning" would probably require the user to perform multiple undo steps, in which case the memory usage would be linear in the number of steps rather than exponential. If the editor supports "Undo to beginning" as a single operation, then it would be reasonable to expect a more sophisticated implementation that does it efficiently -- memory usage can be reduced by sharing the repeated sections of the undo/redo stacks, and it is possible to efficiently skip over undo-redo sequences by recording which states are identical. Exponential memory usage would be indicative of a lazy programmer rather than a fundamental issue with the GRUQ-orithm :)


This doesn't solve the problem of wanting to redo from another "do history branch" while preserving the changes you've done in your current history branch. Using the notation of the article, you have done:

A -> B -> C -> rewound to B -> D -> E

Now at that E point, you'd want to have C too. (I'm assumikng there is no conflict between C and E.) There is no point in the linear history where you have both E and C. Sure you can go back to C, or go back to E. Or go back to C then decide to go back to E again. But you can't have both.

In vim, I use the fact that the yank buffers are not part of the undo/redo history, only changes to the file are. So I undo to the point I had the change I want back, yank them in a named buffer, redo and put the yanked buffer. You cannot go to an alternate history, but at least you can get back stuff you've deleted.

In git, what I describe is pretty-close to cherry-pick from a commit in a branch you deleted that you find using the ref-log.


Would be useful if this had a comparison with Emacs, which also resolves the GURQ.


Emacs already implements exactly what they are proposing: an undo operation is just another action added to the chain of states, which can itself be undone.


Vim supports this out of the box with :earlier and :later


And g+ / g-


I disagree that undo and redo are the only two actions needed to support this behavior. For example, if I’m performing the following three actions:

- insert A

- insert B

- insert C

And then I undo, I would expect to be be back at having AB. If I then undo, I would be undoing my undo and have ABC. Then undoing again would get me back to AB. And I remain in this 2-cycle.

Instead I propose the best solution is to be able to “step out” of the editing experience and see your history in a separate UI, and be able to choose a specific past version to revert to. This revert would be an atomic operation that could then be undone.

This is how most versioned consumer systems I know of work today (e.g. Google Docs, Figma, MS Office) and it feels intuitive to me as a user.


You can do this with a tree-based persistent/functional data-structure, too.

For instance the single writer (per resource) in the evolutionary, append-only database system[1], I'm working on in my spare time, can simply revert the whole resource (resource is like a table in the relational DB jargon) to a past revision. Once a new commit is issued a new revision gets appended and the whole history in-between is preserved. Thus, you can, for instance, retrieve individual changes and cherry pick these from the revisions in-between.

[1] https://github.com/sirixdb


Cool! What about a model based completely on the "history" as a list of states of the document over time?

There is no "undo" or "redo" operation, we only have "browse backward/forward in time".

If you are browsing at a point in the past history, you can just start editing. Then the document state is copied to the end of the history (i.e. the current point in time), followed by your edits, etc.

This removes the 2^n problem. It could be a bit inconvenient if you want to do a lot of tree-like changes, but could also better balance convenience+usability+power.


That is basically the same as the proposed skipping of B in navigation. It does however require the ability to put "make document look like X" (where X is the old state) as a single undo stack entry. If your app design allows that, great, squashing all of B into a single entry would work fine.

Such an implementation would be: if you go back and try to make a change, it moves everything from the redo stack to the undo stack (to return the the "present"), adds one entry in that represents atomically changing the document to match the old state you were looking at, and then finally adds the change you attempted to make as a new entry in the undo stack.


Hmm, I wasn’t thinking of implementing it as an undo stack at all. Anyway, the main difference I’m thinking of is UI and mental model we present, which I think could be a lot simpler here.


Sounds similar to vim’s time travel command

:earlier 30m


This reminds me of the time I nuked all my code by mistake (rm -rf *) and I still had PyCharm open, and I opened their "Code History" tool and un-deleted all my code, immediately proceeded to commit and push whatever state it was in because I was about to freak out.

I think all IDE's should have their own internal copies of code that are housed elsewhere, it has saved me hours of turmoil trying to re-write already solved for code.


I want two things before retirement:

1. visual undo/redo. I don’t want to be surprised when I perform either command. Show me.

2. visual clipboard with history. I don’t want to be surprised when I paste something. Show me. Also let me have more than one item in the clipboard.

Now I know some of you veterans will say, “there’s a plug-in for this! You just need to do these things…” no no no. I want this mainstream. Why are we living in the 1900s still?!


> 1. visual undo/redo. I don’t want to be surprised when I perform either command. Show me.

I'm having a hard time imagining an implementation of this feature that would be faster than "undo, check what happened, redo if undesired". I can see a need to explore the whole history (JetBrains IDEs have the local history), but what benefit do you see from previewing a single step?

> 2. visual clipboard with history. I don’t want to be surprised when I paste something. Show me. Also let me have more than one item in the clipboard.

CopyQ is a great tool that does just this, available on Linux and Windows. I'm sure there's an equivalent for Mac.

EDIT: also, not to plug JetBrains again, but their IDEs have clipboard history built in within the editor. I have CopyQ for system-wide clipboard history, so I don't bother with it, but it's there.


For browsable clipboard history on macOS, I recommend LaunchBar (https://www.obdev.at/products/launchbar/; docs at https://www.obdev.at/resources/launchbar/help/ClipboardHisto...). I used to use the Clipboard Plugin of the free and open source app Quicksilver (https://qsapp.com/), which worked fine but was slightly less streamlined. Some people prefer Alfred (https://www.alfredapp.com/help/features/clipboard/).


Hmm, interesting, but I would actually like a different system (and let me know if any VSCode plugins implement this!): I want TWO forms of undo, one which actually undoes my changes, and one which only shows me a ephemeral view of the code in previous states. Call it "not really undo" or "viewing history but not changing it".


FYI if you use Jetbrains/PyCharm, it also provides this (and has saved me many times)

Just right click your file and view "Local History"


Yup. And it's full-text searchable, which I've found people generally don't realize. Fantastic feature.


In this methodology, without exposing the tree, how does the user select between the two branches created after the butterfly is squashed?

It seems like Google Docs does it in an intuitive way where undo / redo works normally, but there’s another time ordered history of the document to select from.

Is this what the author’s approach is advocating?


In the author’s algorithm there is no two branches. The following diagram is an undo tree where we made change B, went back to A, then made C

      C ——— now
     /
 —— A
     \
      B
But time doesn’t branch, so our history shouldn’t /have to/ branch. What if our undo was itself part of the linear history? Then we could build up the history like this:

  A
  A —— B
  A —— B —- undo_B
  A —— B —— undo_B —— C
For complex undo/redo scenarios this could get out of hand.

  A — B — undo_B — C — undo_C — undo_undo_B —undo_B — undo_A…
But the author (I think correctly) states that for the average user this behavior is intuitive and desired.

They also added the optimization that undoing and then redoing a set of changes doesn’t make it into the history, as it’s basically a no-op.


Nope, the author's solution is to consider the original undo (the "time travelling") to be an undo-able change in itself. No branching history. So you end up with:

  state 1
   * change A
  state 2
   * change B
  state 3
   * undo B
  state 2
   * change C (the "butterfly squashing")
  state 3a
   * undo C
  state 2
   * undo <undo B>
  state 3 (!)
   * undo B
  state 2
   * undo A
  state 1
The traditional editor would differ by putting you right at state 1 at (!), blocking your access to state 3.


there is much more to good undo than this.

many editors collapse edits into a groups and you can only undo by groups, not individual keypresses. This is usually desirable. For example type X then cursor right 60 times then type Y then undo 3 times. Are you at 2 characters before where the Y was or back at the X or before the X? In other words, were the 60 cursor right keypresses collapsed into 1, 2 steps or not collapsed at all

Good editors spend some time thinking about the most expected group definitions, bad editors (vscode in this specific case) seem to have picked a random number for their group size or a even a fixed number of keypresses but it feels random. There's no predicting how much it's going to undo in a single Ctrl/Cmd-z. Ii rarely does what I want compared to other editors


JOE [1] has worked this way as long as I remember (which is close to 20 years).

[1] https://joe-editor.sourceforge.io/


I haven't seen `set undofile` mentioned yet- vim supports persistent undo. You can close a buffer, restart etc and when you reopen, you can still undo. I'm a huge fan.


> you can use up all available memory in approx. 64 quick steps by: Type 1 character; Undo to beginning; Type 1 character; Undo to beginning; etc

This is fine because "undo to beginning" isn't a single action. Each time this loop runs, the user spends more and more time mashing undo to get back to the beginning. The number of actions in the history is only increasing by a constant amount per user action.


This happens to me frequently both in code editing and designing in Adobe suite, needing some of the redo changes after a series of undos. I usually just cut/copy the the parts i want to keep before doing the undos to get to a previous state and paste them back in. Works pretty well.


Lifestreams was based on the same core idea of keeping all past versions of a thing. Only with Lifestreams you keep all past versions of everything. I constantly find myself thinking back on these ideas from the 90s, there's a lot in Eric Freeman and David Gelernter's work that is worth revisiting now that storage is so cheap.


I absolutely detest emacs' default undo behavior. I find it worse and more confusing than the normal simple stupid algorithm. With undo-tree, it becomes better than normal, but only very marginally so. I use the linear undo and redo it provides every day but only go looking at the tree a few times a year.


I've been using undo-tree[0] in Emacs for several years now, and it's amazingly intuitive and fast to use and shows your undo history as a tree.

[0] https://elpa.gnu.org/packages/undo-tree.html


Plug for the Vim undotree plugin, which exposes Vim’s undo tree in a navigable UI. It’s saved my butt in the rare occasion I need it. https://github.com/mbbill/undotree


Relatedly, have you ever thought about how undo works with text?

Open your mail client, compose a message, type a few words. What do you expect cmd-z to do? How much of the words will it undo? All the line? Try it.

Then after undo, type a bit more. Hit enter and cursor back. Type again.

Undoing typing is no trivial matter.


Then there's Undo/Redo for navigating relationships (1994, interactive music video, Macromedia Director, Mark Canter, Meet MediaBand, UnDo Me):

Meet MediaBand (2/5) - UnDo Me

https://www.youtube.com/watch?v=8E-Sc9XafmE

>The second of two interactive music videos on Canter Technology's Meet MediaBand CD-ROM, UnDo Me could very well be considered the "Choose Your Own Adventure" of music videos - in that MediaBand's lead singer, Kelly Gabriel, has to choose from one of four different relationships. YOU help Kelly decide which one to try, and navigate her way through each relationship.

>At the end of each chorus, you are asked to make a branching decision based on whether Kelly should be passive (ice) or aggressive (fire) with the guy she's going out with. Things not turning out well for her? At any time you could "UnDo the past" and choose a different guy, or even jump back to a certain point in the relationship and change your decisions.

>Four separate videos were produced for each relationship (concluding in a total of 16 possible outcomes), connected together with an intro, a home chorus and reverse/"UnDoing" sections. As you could probably tell each section has a different musical style, reflecting the mood and personalities of the relationship that section represents. A version of the song can also be found as a separate CD audio track on the Meet MediaBand CD-ROM.

Meet MediaBand by Marc Canter (1994):

https://www.youtube.com/watch?v=0dRZ52wM84Q

https://www.ebay.com/itm/325099461299


> No, there is no undo "tree"[...]

This is still a tree, it's just implemented in an array.


Wll, it's a tree that is collapsed into an array - which makes it an array to all intents and purposes.

The point is that the "tree-ness" is never exposed to the user. This is the problem OP is claiming to have solved - the cognitive burden that the tree solution introduces.


The point is that the "tree" is not exposed to the user. I feel the author makes this extremely clear.


This is still an acyclic node graph, it's just implemented as a tree.


yep, it's graphs all the way down


Yes a linked list is a degenerate tree, but was this worth pointing out?


So, like Google Doc's document history? When you revert to an old version, it creates a new version with the old contents, it doesn't delete all the intermediate states. (although ctrl-z ctrl-y doesn't do this)


So Back to The Future II basically :-)

(edit below)

(1985 Marty + Doc) => 2015

(2015 Biff) => 1955 => 2015 (he shouldn't be able to return here)

(1985 Marty + Doc) 2015 => 1985' => 1955 => 1985


Sounds like the Braid version of undo-redo.


Game Helpin' Squad: Time Travel Understander

https://www.youtube.com/watch?v=1fABGyVzVwI

[Braid parody tutorial]


Could you elaborate or provide a link? I haven't heard of the "Braid version of undo-redo" before.


Braid is a game about time travel.

This is not like Braid at all, since the game works by "overwriting" your past actions (with a few other mechanics).

The Braid editor would be like "Normal" undo except a ghost cursor continues to make the edits you just undid. Would be a hilarious April fool's feature.



Even if you think you don't like games, Braid was really different


Braid is a video game with time travel as a core mechanic


As soon as I started reading the author's solution, I thought of git. Good to see it acknowledged later on.


Have to applaud the author on their naming game. GURQ is definitely one of the best worst acronyms.


Intellij enables multifile changes, renames, file moves with multiple files as single operation, as well as reload from disk, which breaks this simple linear history model afaiu. There is also a separate linear time-based local history in case you screw up. How intellij handles this is the best, in my mind, because of simplicity and separate "I screwed up" mode.


"Local history" in IntelliJ has saved me several times. The diff mode is very useful when working on a piece of code and suddenly your tests starts failing and you need to find out what changes you made in the last hour.


TL;DR

So much cognitive load.

Just configure Vim to put everything you delete in your clipboard and use a clipboard manager like diodon[1] with 1000 items.

Of course works also outside of Vim, you just have to copy the part you might want to reuse before deleting/modifying.

[1]https://github.com/diodon-dev/diodon


the link to "Klonk" is not found. 404


vim has :earlier 5min and :later 5min commands


Genius.


I am new player


> No, there is no undo "tree", nor any complicated graphical user interface to go with. ... You've got your undos, your redos, and that's it. The underlying data structure is strictly linear, but all edit states are preserved and reachable ...

This is exactly how Emacs works!

Emacs has worked like this since its beginning in 1980s. It was even documented in the first manual (1981):

This might seem to pile one disaster on another, but it doesn't, because vou can always Undo the Undo if it didn’t help. (page 137 of manual)

I think there needs to be some sort of computing and software history lessons. It can offer great value in the current world of software development. It will save you from the trouble of rediscovering techniques that are already in use in classic battle-tested software.

(And really? You need to invent a cheesy acronym for this? If that's what it takes to sell the obvious these days, how about SLUR - Simple Linear Undo Redo?)


Kakoune, like the more logical Vim it is, has both in a sensible arrangement: an undo tree with a current branch, which you can navigate up and down (u/U), and a linear history of the path you took through that tree, which you can navigate backwards and forwards (<a-u>/<a-U>). Unlike a single undo branch, you’re never afraid you’re going to lose your work when you need to go rescue a piece from a previous version; unlike a plain linear history like TFA proposes and Emacs uses, you’re not punished with a quadratic number of undo/redo pairs if you need a point behind several parallel do-overs of the same part. Unfortunately, without any sort of visible representation of all that I frequently find myself getting lost in the whole thing.

(Now that I’m thinking about it, Git’s commit tree and reflog play a similar pair of complementary roles locally.)


> This is exactly how Emacs works!

...and has been since forever. Thus I don't understand what the fuss is?!


Vim also has an undo tree, although you need a plugin to work with it easily (called "Undotree").


Yes but the article is specially calling out that using an undo tree is something no one wants.


It's more of an implementation detail that is available to users. The article's simplification is also available via :earlier and :later, with the added bonus that they can take _time_ as a parameter as well as a simple revision count. “:earlier 1h” has been handy more than once.


And that you still want a linear path through your undos and redos...and that's something that Emacs does.


Of course emacs implements it as just a long linked list.


It doesn't need to be anything else.


In Anathem there were academics who spent their entire lives learning the history of academic scholarship for the express purpose of reminding everyone how often things are reinvented and preventing people from getting too excited when they have discovered something that was already known. I’m drawing a blank on their name, but it was one of the things about the book that I really enjoyed. It really helped to sell the setting, where the scientific method has been known and used continuously for 5,000 years or more.


I blame CADT.


It always astonishes me that undo, redo, and auto-save are implemented without relying on a logging mechanism akin to databases' "write-ahead logs".

To me the main problem of maintaining "redo" actions is of identifying them. They are unnamed, and presenting and recognizing an alteration is hard.


Alternatively, use Vim undotree:

https://github.com/mbbill/undotree


I found the article interesting because it's an approach that I could apply to tools other than text editors - it explains the general concept. I wouldn't have discovered it if someone just posted a link to a Vim add-on.


Or the earlier / later commands which function exactly as the article describes and are part of vanilla vim. You can even give them a time argument, for example :earlier 10m to go back ten minutes.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: