Skip to content

Conversation

@angela-tarantula
Copy link

@angela-tarantula angela-tarantula commented Oct 13, 2025

Fixes #160

TL;DR

DiffBuilder processes operations sequentially and greedily generates Move operations when it detects that a current Remove (or Add) corresponds to a matching Add (or Remove) that occurred earlier in the sequence. For example, if a Remove operation is encountered and a matching Add is found several steps back, DiffBuilder replaces the two with a Move operation. When this happens, the intervening operations, those between the original Add and the new Move, must be reindexed if their 'from' or 'to' depended on the removed operation.

In this issue, a Move operation wasn’t being reindexed because _item_added incorrectly gated reindexing behind the condition if type(op.key) == int and type(key) == int.

What changes

  • In _item_added and _item_removed, determine the parent collection using parent_collection = op.pointer.to_last(self.dst_doc)[0].
  • Reindex only when the parent is a list: if isinstance(parent_collection, MutableSequence). This preserves the original intent but is more accurate and supports subclasses, avoiding issues with numeric-string dictionary keys or mixed structures.
  • Renamed added_item to parent_collection for improved clarity and correctness.

Tests

  • Added test_issue_160: confirms that removing an operation targeting a list correctly triggers _on_undo_add and produces the expected final document.

When an AddOperation would match a prior RemoveOperation, a
MoveOperation is inserted instead, and the RemoveOperation is deleted.
But when the RemoveOperation is deleted, subsequent operations that
depended on it are supposed to have their paths adjusted by
_on_undo_remove. Previously, _on_undo_remove was guarded by type(key) ==
int, which fails to consider cases where the key is a string but the
operation path still needs to be adjusted. The correct guard should be
type(op.pointer.to_last(self.dst_doc)[0]) == list, because
_on_undo_remove should apply whenever the item removed was contained in
a list.
This grants flexility for subclassing. Also removed a comment that
previously existed because of discrepancy between _item_added and
_item_removed that no longer exists.
@angela-tarantula angela-tarantula changed the title DiffBuilder: fix _item_added guard so earlier moves are reindexed DiffBuilder: fix move reindexing Oct 27, 2025
@angela-tarantula angela-tarantula changed the title DiffBuilder: fix move reindexing DiffBuilder: fix operation reindexing Oct 27, 2025
@angela-tarantula
Copy link
Author

@stefankoegl ping

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Patch order sometimes wrong

1 participant