Skip to content

Keep list siblings when undoing a move, and keep the node a drop displaces - #189

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/91-undo-list-sibling
Oct 7, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/91-undo-list-sibling

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #91

Problem

Undo put a node back with AstGraph.MoveTo. When the slot is a sequence, MoveTo replaces the entry at the old index rather than inserting there. Taking the node out had already shifted its next sibling into that index, so undoing a Remove or a Disconnect of a middle statement deleted the statement after it. Connecting a node to a later pin in its own list replaced the wrong entry for the same reason. And the entry a drop replaced left the graph with no trace, so it couldn't be recovered even by undo.

Change

  • AstSchema.TryInsertAt inserts into a sequence and shifts later entries up. It is built from the existing append and detach operations, so it doesn't add another per-slot switch.
  • AstGraph.PutBack is the inverse of MoveTo: same location, insert semantics. Every undo of a remove or a move now uses it.
  • AstGraph.MoveTo keeps its replace-on-a-filled-pin behaviour, with two fixes:
    • It adjusts the index when the node comes from earlier in the same sequence, so it replaces the entry the user actually dropped onto.
    • It leaves the replaced entry as a loose node, the way Replace already treats children it can't move.
  • AstGraphEditor.RecordMove records the displaced occupant. On undo it puts back both the node and the occupant, in ascending order of where each came from, so both indices refer to their original positions again.

For the design question in the triage: I kept the existing default, where a drop onto a filled pin replaces the occupant. The occupant now goes into detached and comes back on undo.

Tests

AstGraphSequenceUndoTests covers the issue's acceptance criteria on a [s0, s1, s2] body:

  • Remove the middle statement, then undo: the body is identical and nothing is detached.
  • Disconnect the middle statement, then undo: same result.
  • Connect Body[0] onto Body[1]: s1 is replaced and stays loose. Undo restores the body exactly, and redo matches the first result.
  • Connect a loose node onto a filled Body[0]: the occupant stays loose. Undo restores the body and leaves the node loose.
  • TryInsertAt shifts later entries up.

With the AstGraph and AstGraphEditor changes reverted, the four undo tests fail. Full suite: 1093 passed, 0 failed. The solution builds with 0 warnings.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AXWSrf4nXKEZF5xVNTiVV5


Generated by Claude Code

claude added 2 commits October 7, 2026 04:35
… displaces [patch]

Undo put a node back with MoveTo, which replaces the entry at the old
index. Taking the node out had already shifted its next sibling into
that index, so undoing a remove or disconnect in a body, parameter or
member list deleted the next sibling. Connecting within a list replaced
the wrong entry for the same reason, and the entry a drop replaced
left the graph with no trace.

- AstSchema.TryInsertAt inserts into a sequence instead of replacing.
- AstGraph.PutBack is MoveTo's inverse and inserts; undo uses it.
- MoveTo adjusts the index for a move later within the same sequence,
  and leaves the entry it replaces as a loose node.
- The editor's recorded move puts that displaced node back on undo.

Fixes #91

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXWSrf4nXKEZF5xVNTiVV5
@sonarqubecloud

sonarqubecloud Bot commented Oct 7, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 3f98fd5 into main Oct 7, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/91-undo-list-sibling branch October 7, 2026 05:38
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.

Undoing Remove/Disconnect in a statement or parameter list deletes the next sibling, and connecting onto a filled sequence pin silently drops a node

2 participants