Do y'all know of any editors with undo/redo behavi...
# thinking-together
j
Do y'all know of any editors with undo/redo behavior that's more interesting/granular than just scrubbing through all of the edits you've done to a file in order? I often find my self wanting "undo the last change to *this function*" 🤔
i
Some 3d modelling tools have separate undo/redo stacks for different parts of the program. For instance, undo/redo for changes to your viewport are separate from undo/redo for changes to objects in your scene, which are separate from undo/redo to changes in your F-curve editor or node-wire material editor.
j
Yeah, that makes sense, sounds like those are basically different editors, that each maintain their own stack
b
I really love vim’s tree based undo. It’s like git for keystrokes - edits on top of edits create branching histories.
It isn’t localized like you describe. Also impossible to use without a plugin to visualize the tree, like Gundo.vim:

https://advancedweb.hu/assets/posts/vim-undo-tree/gundo-1b34fafe3700f571c821123a52eaf29d43dae7c72aa88e04b4dd42916ae3ee2a.jpg

. “Local change history” is a really cool idea; I think I would struggle to define the boundaries for my undo-space in a consistent way. Sometimes I’m working on one function / module; sometimes I’m pushing larger globs of code around.
j
Yeah, there would definitely be changes at different levels, it'll be a challenge to represent things in a way that doesn't break either the large or the small case.
One hacky idea is to have the main history stack/tree be for the whole editor, but have a "view history of this function" and allow you to "revert" to a previous state, which actually adds a change to the main history stack.
b
Thinking about a version control system which understands code structure led me to visualize it as basically node/wire visual programming, where undo histories are held both at the node level (function, module, class) and for the overall canvas (file, project?). I think that’s pretty much what you described. Rephrasing a bit, maybe one way to think about it is expanding the hierarchy of undo scopes: most people today have project scope (version control) and file scope (editor undo). Higher up the hierarchy than project scope - integration scope, maybe? - depends on monorepo; I guess API versioning and pinning is kind of this as well. Lower down the hierarchy is function edit history.
i
Solidworks has a UI tree that sound like the opposite side of vim’s graph. There’s a command history where you can go to any previous point and change a parameter, if it still creates a valid model, changes trickle down. Porting that to a code editor could be something like: if x is editted on commit 4 and there are no errors; then merge to present; else (attempt to edit in between commits so some test is satisfied?)
b
Oh interesting. So kind of a “back to the future” time traveling editor model. Is there a challenge with deep historical changes having unintentional outcomes after the changes propagate? when you “undo”, does it undo the change you made to the historical command, or remove the effects of the most recent command (leaving the “altered history” in place)?
i
Yes, going further back tends generally means more commands depending on each other with potential to break. I think the best description would be “modifying” a previous command (which could include a delete/hide). After that it attempts to rebuild to current state, or as far as it can get before something breaks. There is also a normal CTRL+Z which you rarely use as a “good” Solidworks user.
j
Amusingly, Observable lacks global undo, but because CodeMirror implements nice undo functionality, it still has per-cell-editor undo. It’s granular undo by default / accident.
a
A possible UI for text would be to select an arbitrary region (maybe a function, maybe bigger/smaller) and undo changes that intersect with that region. Or use said selection as a filter for other fancy operations on undo history. (Which presumably gets really interesting when you try to have undo history for the selection itself, a la Blender and what I wish I had in Kakoune (vim-like selection heavy editor)). In practice, I tend to delete by commenting until I'm ready to commit to Git, so I can undo either by reverting or uncommenting. Which is to say, I'm interested in this feature. 🫠
k
My reaction was the same as @Joshua Horowitz. Organizing code by definition is an easy and intuitive way to get per function undo. Having multiple kinds of undo seems like it might be confusing some of the time.
j
I use the emacs version of that tree-based undo system. It's very cool, but not granular in the way you want. IIRC the Smalltalk environment I used in the late 80s had scoped undo in the individual editors, which is more along these lines.
c
I think you might be able to get this from git if you pushed every single change as a commit and used interactive rebase
(I mean do that behind the scenes as a way of implementing it)
g
I've implemented an undo/redo system on top of mercurial before when building an in-browser editor I believe GIMP has had for a long time planned an event-sourcing style system where you are recording actions not state and can add or remove actions to recreate state at any time. This means that you can do things like build up a series of operations, then go and remove some of the operations in the middle, or maybe even change the image you're operating on without redoing the operations. GIMP still hasn't implemented this, but I believe it is how FreeCAD works
and also yes, I also use
undo-tree
in emacs
o
maybe a poor-man and somewhat pragmatic approach would be to just have each function in a separate file, then you get undo per function with standard out of the shelf solution
j
fortunately for me I'm throwing out the "code lives in text files" thing altogether, so I have quite a bit of flexibility 😄