Open Zalastax opened 7 years ago
This would be extremely useful.
Quite cool!
Perhaps I'm biased from looking at git branches too often, but I sort of like Gundo's approach to rendering the undo tree (especially the top->bottom -- present->past sort order).
I'm not sure what the nodes should be, should they be undo stops (perhaps a bit too many of them), should they be time-based, should we change the undo stops themselves to be time based... I think these are a bit too hard to decide upfront. I also think this feature could be useful for linear undo histories (I'd use it even if the undo tree has no "forks" as a way to look back at what I've done)
As for implementing it, I think you're on the right path w.r.t. the data model to represent the undo tree. For the UI, I'd start a new folder in editor/contrib/
and start easy with an editor widget (perhaps an overlay widget). I'd look into rendering the tree using a <canvas>
, and I'd try to make the editor core API (what will eventually make it to IModel
in editorCommon
) as slim as possible -- the idea is the editor can work just fine without any of the /contrib/
(they're sort of plugins) and this should be a plugin in that sense.
Definitely a nice idea!
@alexandrudima I'd like some new feedback or hand the code over. https://github.com/Zalastax/vscode/commit/d7f0b8496c6b17479d92e56cf34479cd37acdf1c implements the model changes and a contrib plugin with nice rendering. Things I know need to be improved are:
I have completed the parts I find interesting so I'd be happy to let someone else finish this up, but there would be no problem if I have to do that myself. Let me know what you think and what the next step is!
Render of the history tree using a light theme:
Small improvements and keeping up with upstream in https://github.com/Zalastax/vscode/commit/83359d5e5c5326836a1be04f7af674d51135d426. Added:
Thanks for your hard work on this @Zalastax, we really appreciate the effort you have put into this. Please accept my apologies for not getting back to you earlier.
I think this is a great idea and it is one that I think most people would react positively to when it was described to them.
Crucially though, I wonder if it is something that many people would modify their behaviour to start to use. The UX and UI for this would have to integrate well with current behaviour, otherwise I expect many people just wouldn’t adjust. For example, if, in order to take advantage of this, you had to learn another keybinding on top of ctrl-Z, most people would just keep using ctrl-Z because that is such a habit.
On the other hand, if it was integrated with the undo command today, that would increase usage, but we would need to do it in such a way that people don’t notice it most of the time. Ctrl-Z would continue to work as expected until there is a branch when we would need to get them to tell us which branch to take. This is a challenging UX to design.
So I think if we make it a separate feature to undo, a small number of people might adjust their regular behaviour to start using it. If we worked on the UX so that it integrated with existing usage patterns, more people would use it, but we would run a higher risk of breaking a fundamental editor experience if we didn’t get the UX right.
Unfortunately we do not have the capacity to focus on designing this user experience. So given this, sadly we will have to decline this feature request. I understand that this is disappointing news and I can only apologise again for not responding earlier.
I'm disappointed and perplexed. These news come after I put the work in after getting greenlit. We already have examples of this working in Vim and Emacs, and it's fairly usable already in my fork. You don't use this feature all the time, but when you do it's indispensable. Normal undo is undisturbed and when you need it you bring up the undo tree. I think we would only need to polish the mouse and keyboard interaction of what is already there in the fork to provide good value with a small effort. I really hope you will reconsider.
@stevencl Any way to get you to reconsider? How about a pep talk.
Frankly, I believe you misinterpreted how it might work.
UI wise:
What if we work more heavily on the UI? I gather this wouldn't be very doable as a plugin, right? I'm actually willing to help maintain and update a fork if @Zalastax's is okay with that. This is the one big feature I've been waiting for. The VSCodeVim plugin maintains its own undo/redo history that diverges from VSCodes, and I know that they have valid reasons for doing so, but it's so occasionally annoying.
even if this doesn't work out, I still love VSCode and all the work the team puts in. Thank you for this beautiful piece of software.
@chuckdries you're very welcome to do so, and I'll be happy to help you maintain a fork. I'll fix my fork so it works on top of master. You can email me if you want to get in touch on some other platform.
I'd be more than happy to chat. As I said, I think this is a great idea.
@stevencl What about exposing the undo tree for extensions and keeping the current UX inside standard vscode unchanged?
I understand from a UX perspective this is a very hard change to make, and there possibly might not be one correct solution to the problem.
The API though (as far as I understand it) is actually rather simple. All that is needed is a getTree()
and setState(node)
. With a little more thought (and chatting to the vscode-vim group) it might even allow a solution to the VSCodeVim/Vim#1490 regarding syncing their undo stacks.
The VS editor can always walk the right-most edge (which is equivalent to how undo/redo works at the moment) and it'd allow experimentation with the UI from extensions.
Motivation: For some of us though this undo tree's mean not losing work. Personally I'm using a voice programming system and mistakes are frequent though very rarely destructive. Without having an undo-tree in the editor a mistake during any kind of undo operation runs the risk of being destructive and losing history.
I won't have time to maintain my fork for several months but if anyone wants to step up to make this happen I'll help. There are further details for what needs to be done in the issue section of my fork. I think it's important to have a team member help at least design the API, since the details are quite intricate. For this to get done there needs to be clear and fast communication from all parties, which is not happening now.
I'd like to voice my support for this. It would be an excellent addition to the editor (we've all accidentally blown away our undo history!). As mentioned above, it in no way affects how current undo stack works - it is transparent. And finally, @Zalastax has kindly implemented the foundations of the feature!
Seems like a no-brainer?
@stevencl I use undo-tree in Emacs on a daily basis, and the key thing to understand about this feature is that when your editor already has a normal undo stack (as VSCode already does), adding a tree based undo UI on top of it is completely compatible and transparent with the standard undo and redo commands. If the tree is not open, an undo will go up a node in the tree, and a redo will go down the most recently chosen branch. Because the most recently chosen branch is the most recently created branch by default, the default branch chosen will be exactly the same whether or not a tree based undo stack is used in the implementation, even if the undo tree is never used and the user is unaware that it exists. This is not a UX problem because if the undo tree is not opened the editor will behave exactly the same with undo/tree, but an internal undo tree implementation is necessary to allow for this new undo tree UX to be added. Please reconsider.
I'd really like to have this.
One idea for the UI:
When undoing multiple changes I hold Ctrl pressed and press Z as often as necessary.
So when pressing Z we could show an overlay (maybe over the Explorer) that is as simple as the Emacs one and keep it visible as long as Ctrl is held. Pressing Z and Y works as always and navigates through the tree. Using the cursor keys allows selecting alternative branches.
Example:
Pressing Ctrl + Z undoes the last change and shows the undo tree. Still holding Ctrl pressed and pressing the arrow down key undoes another change. Still holding Ctrl pressed and pressing the arrow right key highlights an alternative branch in the undo tree. Still holding Ctrl pressed and pressing the arrow up key redoes the first change from the new branch.
Z and arrow down as well as Y and arrow up can be used interchangeably.
@gralphi that's not a bad idea. I really like how holding control is what keeps the ui open. However, isn't there a risk that it will feel like the UI is flickering if you just do a normal undo? Having a special key combo to open the UI seems preferable to me, perhaps ctrl+alt+z? Keep the good ideas coming and we'll have a good base once all parties have time to get involved again!
Flickering is a valid point.
Maybe it should pop up when one of the following conditions is met (pressing Ctrl+Z and holding Ctrl down is precondition to all of them):
Would have two advantages: Users wouldn't need to memorize a new combination and they will sooner or later discover the new functionality by accident.
Having a tree overlay that pops up after a normal undo/redo is an interesting idea, but I don't think it should be enabled by default because undo trees can be confusing and it's not obvious that normal undo/redo works the same in a tree, as per my previous explanation. I also don't think the user should have to hold ctrl to get related undo commands, that would get confusing when the other binds do things unrelated to undo because the keybinds would become modal, and I think that's less intuitive.
By default, the undo-tree
Emacs package does not override the main undo bind, instead it only replaces one of the less common keybinds for undo so you can still use both. The alternative keybind then opens the tree without starting an undo yet. I think we should do something similar and add a new keybind for the undo tree to avoid confusing existing users. This would make the feature harder to find, but it could always be added to the documentation and following the principle of least surprise is more important than enabling more advanced features by default in my opinion. We could also add a separate command for the tree based undo so if a user searches the commands for undo, they would see both standard undo/redo and the command that opens the tree based UI.
@nickmccurdy I totally agree. The biggest question marks when I stopped working were:
See https://github.com/Zalastax/vscode/issues for more details.
With the new webview API coming up, the view API seems to be solved. The view can now be a webpage that listens to commands from the main editor and updates the view accordingly. Thus, the biggest remaining hurdle is designing the API for the undo tree information. I still don't have much time to commit to this, but I can help discuss your ideas if someone steps up.
I really hope we will see this functionality in VSCode in the near future. @stevencl, since you have expressed your willingness to chat about this, could you offer a response to the points chuckdries made? This seems to me to be an extra feature that doesn't break existing user behaviours, and can simply be used in addition to them for users who would benefit from this feature. Even if a significant proportion of VSCode developers remain unaware of this feature, undo trees have the potential to recover large quantities of accidentally deleted work. Even if only a few % of vscode users ever use this feature, that would still be saving many many hours of lost work.
Further, this exact functionality is already present in many popular competing editors/IDEs, such as JetBrains' products, Brackets, Vim and Emacs. With the inclusion of undo-tree support, there would be few remaining compelling reasons to choose anything other than VSCode.
Sorry for not responding earlier.
I understand how this can work in addition to regular undo (indeed, I used zalastax's fork to play with the experience before responding back in September last year so I have seen how it might work). And I understand the value that undo branches provides. My reluctance to take on this work is not based on a lack of understanding of undo branches or their value.
As the UX researcher on the team, my goal is to optimise the experience that we ship out of the box for as many users as we possibly can. Adding extra commands and functionality always comes at a cost, no matter how useful the command and functionality is (at the very least, extra commands increase the search space that new users must search through when looking for functionality). So we are always very careful about adding anything to the product. Consequently, my point about investigating how or if this could integrate with the regular undo experience was motivated by this goal.
That being said, I think that the comparison to single cursor/multi cursor is fair and I do believe that undo branches could live alongside regular undo. However, we would need to spend time carefully designing the UX for undo branches. For example, how should they be made visible (in a document/a viewlet/an overlay/a panel/something else)? How can we integrate them in a way that is consistent with other UI in the product? Additionally, we would need to spend time designing the UX for undo branches so that the user is always very clear about the changes that will be made as they navigate the undo tree. We would also need to consider how this would work with other extensions (such as LiveShare: see this issue for example https://github.com/MicrosoftDocs/live-share/issues/7).
This isn't a trivial amount of UX work although none of it is impossibly difficult UX work of course. It's just that we do not have resources to do this work as we are focused on other tasks. In addition, I think that it's not a trivial amount of engineering work to integrate this, although that is not my area of expertise. So unfortunately this is just not something that we are in a position to work on.
I like how IntelliJ handles a similar use-case via local history. Rather than showing a tree of actions/unactions, it shows a linear history of a version of a file, not unlike git reflog:
Basically, there's a separate action to show a history of a file, which allows to revert to any version, showing a helpful diff with the current state as well.
What's so hard about this? Ctrl + Z = undo. Ctrl + Y = redo. Ctrl+Shift + Z = Previous Top undo state Ctrl+Alt + Z = Next Top undo state.
Every time you change undo branch a small popup (showing a tree as illustrated above) on the bottom or right of the page pops up for 500ms and animates your change, then immediately disappears, and ofc, you can turn it off.
@anActualCow that's a good UI suggestion in my opinion. Unfortunately, there are other technical questions that need to be answered. Keep the buzz up and hopefully this will get prioritized one day.
Just want to add another voice in support of this request. It's one of my most missed features coming from neovim (w/ Mundo). I honestly, doubt many people even realize that the write & undo experience of an editor could be so much better. Even for nonpower-users this handles the case of accidentally obliterating the redo-history with a miskey-press.
As a long-time Emacs and undo-tree user, and someone who really misses the ability to easily navigate the undo tree, I'm extremely interested in this and support this feature as well.
This is one of the killer features of Vim; it would be great to have it in VSCode. Sorry for the noise, but I want to add my voice to the chorus.
Could the the new Timeline view be used to support this feature? Or does it only support "path graphs" (aka "linear graphs"), i.e. graphs where nodes can have at most one predecessor and at most one successor?
Note that the "undo graph" (aka "history graph" aka "undo branches") is usually at least a tree, if not a general graph (if we assume a graph where any nodes with the same text collapse into one node)
Ohhhh yes, please do consider adding the tree view like in Emacs as mentioned by @Zalastax and @amelio-vazquez-reina . It's super intuitive and allows you write and rewrite code much more easily, because you know that what you wrote before can always be retrieved. It's a bit like a git commit for every keystroke. :-) :-) :-)
@thijsbro If you are interested in this feature, I recommend watching / following up / upvoting on #84297 as well since the new Timeline view as I mentioned here could potentially help here :)
@amelio-vazquez-reina thanks. Really wanting this feature
@alexdima and @stevencl do we still have technological challenges for implementing this, like LiveShare? I see that https://github.com/MicrosoftDocs/live-share/issues/7 is closed. Let me know if you want to have a second go at this. I think it would bring a lot of value.
I'd like propose another shortcut. Undo/Redo shortcuts should work linearly as they do now. Editor should know which branch the user has backtracked from and upon redoing should go back to it. But have additional shortcuts to cycle through branches. So one for up and one for down. Pressing these shortcuts will take you leaf nodes of sibling branches.
Summary of shortcuts:
This doesn't require GUI, GUI is nice to have but not required.
Visual Explanation
Furthermore, it'd be useful for last edited branch to be the most bottom branch. This way when you cycle back you always go in descending order of last modified.
There is some vagueness in ordering branch by last modified, probably best figured out by testing it
IMHO I don't think it's needed to get used to any keyboard shortcuts. Maybe add some for the elite who only uses keyboard but really a graph is enough for when I accidentally press a key when trying to get some value back.
Just observed my co-worker using vscode lose some of the changes because they rolled back with ctrl+z to see something and then touched some key which dropped all future history. From a perspective of an emacs user who has undo-tree, that was hilarious, sorry 😂. Point is, it's one of the features that you use once and can't even imagine living without it.
As for keyboard shortcuts, there is no need to overthink it. The only shortcut that is needed is something that'd drop you into a tree view (ctrl+shift+z seems like an obvious choice, if that's unused, if it is then maybe alt instead of shift). From there it's arrows only: ↑ to go back, ↓ to go forward, and ← → to switch between the branches that come out of the current point. Esc to close the tree view. Doing it sideways is also fine, but history is more vertical than horizontal (many changes, not so many branches), and vertical scrolling should probably be preferred.
Local history is now in the timeline.
I think if we enhanced local history to include undo tree, there is a unified ux.
The undo tree would get collapsed into new local history items periodically
Just wanted to add my support for this feature. It's not often used, but very useful when it is.
As a potentially helpful reference, this is the code for the undo feature in neovim: https://github.com/neovim/neovim/blob/master/src/nvim/undo.c
orpheuslummis Gold mine, Thanks
I'd like to add my support for this feature. I would argue that this feature would be very widely used for a variety of reasons. I would use this feature multiple times a day, possibly hourly for work and personal use. It's something that not only already exists in other IDEs, but also tools from Serif like Affinity, and it's an incredibly helpful feature when you're debugging, or troubleshooting.
Given the interest here I wanted to mention an idea I had for implementing undo tree as a standalone utility which I made a a separate gist for. Basic idea is to create an undo tree with some clever git usage.
If anyone wants to discuss the idea please do so there as it is not germane to this thread.
Just want to chime in my support for this feature. I think undo trees can be extremely useful!
We definitely need this! This is an essential feature found in all good editors.
+1 for this feature! this is still missing in 2024
this the only thing i miss from vim please heavily consider this !
It's been a while but super keen to see this implemented!
Undo branches / undo tree allows you to undo some changes, then make a new change, while keeping all changes available in the undo tree. The undo tree can therefore prevent awkward scenarios such as:
This screen-cap showcases how it works in Emacs with undo-tree.
https://github.com/Zalastax/vscode/commit/c5699711d21dece88c7c608f6d8ae277f72a0081 contains the changes to the model that are needed, but several questions remain to be answered. I'm interested in completing the feature but need further input before proceeding.
You can try out the current changes by compiling my fork and you'll find 'History Tree' in the Edit menu. Clicking the menu item will console.log a function that changes what redo future is selected (just like in the gif).