Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting data doesn't clear undo stack #4060

Closed
Reinmar opened this issue Apr 28, 2017 · 14 comments · Fixed by #7650
Closed

Setting data doesn't clear undo stack #4060

Reinmar opened this issue Apr 28, 2017 · 14 comments · Fixed by #7650
Assignees
Labels
package:engine status:discussion type:bug This issue reports a buggy (incorrect) behavior.

Comments

@Reinmar
Copy link
Member

Reinmar commented Apr 28, 2017

  1. Open any sample.
  2. Type few letters (or do any other change).
  3. Call editor.setData( '<p>foo</p>' );
  4. See that undo is enabled.
  5. Undo. Error is thrown.

We have two things here:

  • It should be possible to manage whether setting data resets the history (whether it's undoable or not) or not. Or, like in CKE4, there should be a way to tell whether setData() should create undo step and a way to clear the stack. This is more granular but boils down to the same story.

  • If setData() was meant to create an undo step then undoing shouldn't throw an error.

    I also thought that features like typing may need to reset their stored batch on such actions. I think that ChangeBuffer should be doing this reliably now but it may be good to write a test for it (if there's none).

@Reinmar
Copy link
Member Author

Reinmar commented Jun 21, 2017

@scofalik wrote in another ticket:

We wanted initial setData to be not undoable. This makes sense. You don't want to have an undo step just after loading editor. To manage this, batch created in editor#setData is transparent.

However this breaks later setData calls. Imagine initial batch, then some changes, then setData and then when you undo you will try to undo "some changes" - in best case scenario nothing will happen. In worst case scenario something will blow up because new editor contents have nothing to do with old, after setData.

My proposal is that:

  • setData should somehow clear undo stack. I don't know how to connect those two in reasonable way, but probably setData would have to just check whether undo plugin is available and just call appropriate method, or
  • make setData create non-transparent batches by default but add an option to change it.

BTW. I know that in perfect world, with super-stable undo nothing bad should happen. But ATM editor throws in this scenario (when restoring selection). We don't have to make those changes now but it's good to have this problem in mind.

@Reinmar
Copy link
Member Author

Reinmar commented Jul 13, 2017

In ckeditor/ckeditor5-paragraph#27 we noticed that we stopped autoparagraphing on setData() because setData() uses transparent batches. Initial autoparagraphing works now because it's triggered on dataReady. Then, it only works if e.g. someone mistakenly remove the entire content, but not if that change was transparent (e.g. done by setData()).

@Reinmar
Copy link
Member Author

Reinmar commented Jan 16, 2018

The above is not true anymore because now post-fixers don't check the type of batch.

@Reinmar
Copy link
Member Author

Reinmar commented Jul 13, 2018

I'm raising the priority of this ticket. It'd be good to first gather some feedback. What options do developers need? What API will be most useful?

In https://github.com/ckeditor/ckeditor5-autosave/issues/6 we've seen people doing subsequent seData() calls.

@Reinmar
Copy link
Member Author

Reinmar commented Jul 13, 2018

In https://github.com/ckeditor/ckeditor5-autosave/issues/6#issuecomment-404776461 I commented on the proposal to have setData() and initData(). Right now, I don't see many use cases for the former semantics, so I'm against having two separate methods.

I'd rather see something like:

editor.setData( data ); // does "init data"
editor.setData( data, { mode: 'change' } ); // does "set data as nothing happened, keep undo, etc."

@pjasiun
Copy link

pjasiun commented Dec 5, 2018

Working on the comments integration we were also missing the option to re-init editors data. With @oskarwrobel it took us a while to find a way to load comments and content in the way which does not look like a total hack, and we are still not very happy with what we get. Loading data should be simple and there should be no hacks needed.

It is not only about cleaning the undo stack, but also about resetting some plugins (remove pending actions, stop uploads, remove comments, etc.). Also, note that collaborative editing feature will integrate differently with it (basically will not allow you to re-init data if the collaborative editing plugin is enabled).

Since we already have editor.data.init method we should make it clean undo stack and fire special event which causes reset plugins data.

When it comes to the editor.setData interface, I agree that there are no many use cases for setData without resetting data (I can not imagine any), and we could have:

editor.setData( data ); // -> editor.data.init
editor.setData( data, { mode: 'change' } ); // -> editor.data.set

But I am not sure if it won't be missleading :/

@Reinmar
Copy link
Member Author

Reinmar commented Dec 5, 2018

I think we all agree that the main usecase for setData() is resetting the editor, so that's a sensible default. I'm ok with doing this change. However, editor.data.init() is asynchronous so bam, we can't use it here.

@scofalik
Copy link
Contributor

Since we already have editor.data.init() I don't understand why setData() uses transparent batch.

It should either use normal batch and let undoing (makes sense) or it should re-init the whole editor, i.e. use transparent batch, clear history and fire an event on which plugins can listen and clear themselves.

Now we have the worst option, TBH, which is prone to generate errors.

@oskarwrobel
Copy link
Contributor

oskarwrobel commented Dec 18, 2018

or it should re-init the whole editor, i.e. use transparent batch, clear history and fire an event on which plugins can listen and clear themselves.

👍

@Reinmar
Copy link
Member Author

Reinmar commented Apr 19, 2019

@Reinmar
Copy link
Member Author

Reinmar commented Apr 19, 2019

It'd be good to know if there's some quick workaround - a method or something you can do after calling setData() to clear the undo stack. cc @scofalik

@scofalik
Copy link
Contributor

Citing myself from Gitter:

Well, _stack property in BaseCommand in undo is protected so you can't do that nicely. If you don't care and the issue is important to you, then you can simply clear _stack array in UndoCommand and RedoCommand (get the commands through editor.commands.get( 'undo' ) and 'redo'. This is, of course, not recommended.

Out of the top of my head, there's one small issue with that - removed nodes, which are stored in $graveyard root (and are no longer needed as you cleared undo stack) won't be cleared which is a memory leak issue. Clearing them is again a thing that there's no public API for, though.

The go-to solution would be, in my opinion:

  1. Fire an event on setData or decorate it.
  2. Have all interested plugins (like undo) listen to that event and clean themselves on it.

For now I am afraid that there's no nice solution.

@mlewand mlewand transferred this issue from ckeditor/ckeditor5-engine Oct 9, 2019
@mlewand mlewand added this to the nice-to-have milestone Oct 9, 2019
@mlewand mlewand added status:confirmed type:bug This issue reports a buggy (incorrect) behavior. package:engine labels Oct 9, 2019
@baturian
Copy link

Any changes in that issue? Still can't clear undo stack by public API. And setData() still doesn't clear undo stack as well.

@Reinmar Reinmar modified the milestones: nice-to-have, iteration 34 Jul 16, 2020
@Reinmar
Copy link
Member Author

Reinmar commented Jul 16, 2020

@baturian not yet, but we've just added this ticket to the sprint so most likely it will land at the end of July.

The decision was to have setData() always clear the undo stack. We can't really think about valid use cases for not clearing it.

We want to implement a solution described by #4060 (comment). It's not yet clear whether DataController#set() should fire a new event or whether it's enough if it will be decorated (as DataController#init() is already).

jodator added a commit that referenced this issue Jul 22, 2020
Fix (undo): Undo/redo stacks should be cleared on `DataController#set()`. Closes #4060.
Feature (engine): The method `DataController#set()` is now decorated so plugins can listen to `editor.setData()` calls.

MAJOR BREAKING CHANGE: The method `editor.setData()` clears the undo and redo stacks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package:engine status:discussion type:bug This issue reports a buggy (incorrect) behavior.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants