Skip to content

Keep a command a composite in the history holds out of CommandsDiscarded [patch] - #181

Open
matt-edmondson wants to merge 1 commit into
mainfrom
fix/reachable-discards-180
Open

matt-edmondson wants to merge 1 commit into
mainfrom
fix/reachable-discards-180

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #180

What was wrong

OnCommandsDiscarded seeded its "still in the history" set from _stackManager.Commands, which holds only the top-level commands. A command held inside a CompositeCommand in the history didn't count, so it was reported when its own slot left. Only the Merged path had protection, through the CanReach filter in ExecuteMerged. The issue's repro (execute a and b, undo both, then execute CompositeCommand("group", [a, b])) reported a and b as Branched. The README's handler then disposed them, and group's next Undo or Redo ran disposed commands. Replaced and Trimmed had the same gap.

Change

  • A new ReachableFromHistory() walks the history's top-level commands and, recursively, the sub-commands of every composite among them. It keeps a visited set, so it walks a shared composite only once.
  • OnCommandsDiscarded seeds unavailable from that walk, so Branched, Trimmed, Replaced and Cleared all get the same protection.
  • The Merged-only CanReach filter in ExecuteMerged is now redundant but harmless. I left it in place so this PR doesn't conflict with Ask CanMergeWith once, and drop a merged-into command a failed merge leaves unapplied [minor] #176, which reworks that method.

Tests

New CompositeDiscardTests with one test per acceptance criterion, plus one guard:

  • GroupingUndoneEdits_DoesNotReportThemAsBranched: the issue's repro reports nothing.
  • GroupingUndoneEdits_StillReportsTheEditsLeftOut: grouping only a, inside a nested composite, still reports b as Branched and keeps a.
  • RestoringAStateWhoseCompositeHoldsACurrentCommand_DoesNotReportItAsReplaced: restoring [gx] over x.
  • DroppingTheOldestCommand_… and TrimmingToMaxStackSize_…: a Trimmed x that a remaining composite holds isn't reported.

With the service change reverted, all 5 new tests fail. With it, the full suite passes: 211/211 on Linux / .NET 10.


Generated by Claude Code

…ded [patch]

OnCommandsDiscarded counted only the top-level commands as still in the
history, so a command held by a composite in the history was reported
when its own slot left. Grouping undone edits into a CompositeCommand
reported them as Branched, and the README's dispose handler disposed
commands the composite still runs. Replaced and Trimmed had the same gap.

The history now counts every command reachable from it, walking nested
composites, for every discard reason.

Fixes #180

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

sonarqubecloud Bot commented Nov 6, 2026

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

2 participants