Skip to content

Undo manager sometimes generates wrong states when undoing #736

@javiergonzalez-synth

Description

@javiergonzalez-synth

Please save me some time and use the following template. In 90% of all issues I can't reproduce the problem because I don't know what exactly you are doing, in which environment, or which y-* version is responsible. Just use the following template even if you think the problem is obvious.

Checklist

Describe the bug

Undo-manager over arrays sometimes generates a wrong result when undoing.

To Reproduce
Run the following jest test:

import * as Y from "yjs";

it("undo should be ok", () => {
  const doc = new Y.Doc();
  const array = doc.getArray("array");
  const undoManager = new Y.UndoManager(array, { captureTimeout: 0 });

  doc.transact(() => {
    array.insert(0, [1, 2, 3]);
  });
  // 1,2,3
  expect(array.toJSON()).toEqual([1, 2, 3]);

  doc.transact(() => {
    array.insert(2, [6, 7]);
  });
  // 1,2,3
  // 1,2,6,7,3
  expect(array.toJSON()).toEqual([1, 2, 6, 7, 3]);

  doc.transact(() => {
    array.delete(1, 1);
    array.insert(1, [8]);
  });
  // 1,2,3
  // 1,2,6,7,3
  // 1,8,6,7,3
  expect(array.toJSON()).toEqual([1, 8, 6, 7, 3]);

  doc.transact(() => {
    array.delete(0, 1);
  });
  // 1,2,3
  // 1,2,6,7,3
  // 1,8,6,7,3
  // 8,6,7,3
  expect(array.toJSON()).toEqual([8, 6, 7, 3]);

  doc.transact(() => {
    array.delete(1, 2);
  });
  // 1,2,3
  // 1,2,6,7,3
  // 1,8,6,7,3
  // 8,6,7,3
  // 8,3
  expect(array.toJSON()).toEqual([8, 3]);

  undoManager.undo();
  // 1,2,3
  // 1,2,6,7,3
  // 1,8,6,7,3
  // 8,6,7,3
  expect(array.toJSON()).toEqual([8, 6, 7, 3]);

  doc.transact(() => {
    array.insert(2, [9]);
  });
  // 1,2,3
  // 1,2,6,7,3
  // 1,8,6,7,3
  // 8,6,7,3
  // 8,6,9,7,3
  expect(array.toJSON()).toEqual([8, 6, 9, 7, 3]);

  undoManager.undo();
  // 1,2,3
  // 1,2,6,7,3
  // 1,8,6,7,3
  // 8,6,7,3
  expect(array.toJSON()).toEqual([8, 6, 7, 3]);

  undoManager.undo();
  // 1,2,3
  // 1,2,6,7,3
  // 1,8,6,7,3
  expect(array.toJSON()).toEqual([1, 8, 6, 7, 3]);

  undoManager.undo();
  // 1,2,3
  // 1,2,6,7,3
  expect(array.toJSON()).toEqual([1, 2, 6, 7, 3]);

  undoManager.undo();
  // 1,2,3
  expect(array.toJSON()).toEqual([1, 2, 3]); // !! actually has 1,2,7,3
});

Expected behavior
Undo manager should be able to go back to the first initial state, 1,2,3, but it goest to a weird 1,2,7,3 state instead. This is the minimal amount of operations I could find that trigger this error.

Environment Information

  • Node 20.6.0
  • Yjs version 13.6.27 and 14.0.0-8

Additional context
The problem with this kind of broken undo steps is that you never know exactly when they are going to trigger and it leaves you in a potentially corrupted state

  • I'm a sponsor 💖
  • This issue is a blocker for my project.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions