fix: preserve stack below when deleting a top stack block#3571
fix: preserve stack below when deleting a top stack block#3571Joeclinton1 wants to merge 4 commits intoscratchfoundation:developfrom
Conversation
|
This was another GPT 5.5 fast test in codex. It took 12 minutes from prompt to pr. This time I tested it better to be sure it actually fixed it, as the previous with the shadow block duplication I messed up the repro, and thought it had fixed it when it hadn't. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a Scratch-specific deletion bug where deleting a top-of-stack block (via context menu or keyboard) incorrectly deletes the entire stack below it, by detaching the nextConnection before disposal and routing both delete paths through a shared helper.
Changes:
- Added a shared
deleteBlockhelper to centralize delete behavior and preserve the stack below when deleting a top stack block. - Overrode
Blockly.BlockSvg.prototype.checkAndDeleteto use the same delete logic for keyboard Delete/Backspace. - Added unit tests covering both the context-menu delete callback and the keyboard-delete wiring.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/context_menu_items.ts |
Extracts deleteBlock helper and updates the context-menu delete callback to use it. |
src/index.ts |
Routes keyboard delete (checkAndDelete) through the new shared helper. |
tests/unit/context_menu_items.test.ts |
Adds unit tests ensuring deleting a top stack block preserves the next block for both delete paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
cwillisf
left a comment
There was a problem hiding this comment.
I like how careful deleteBlock is. Thanks for the help!
cwillisf
left a comment
There was a problem hiding this comment.
OK, the Copilot review has some good points...
|
(I recommend rebasing on or merging from |
Sure let me see what I can do, I will get a second version out soon. |
a5731f2 to
2b22237
Compare
|
@cwillisf I tested the proposed bugs by the reviewer and they don't exist. So I can do these changes but you can't reach the proposed bugs. Also you can delete a custom block with delete blocks even though it has usages which is a new bug, and not caused by this change (i tested in production). |
|
I did the changes related to non-deletable blocks can't test because, the only non deletable is the procedure definition and that's currently deletable in production which is an upstream bug. Shadow block inputs weren't deletable with keyboard, maybe because you can never focus them in such a way that would let that be possible The undo group thing can't replicate so didn't fix. |
It can cause issues if someone writes code like this in the future: Blockly.Events.setGroup(true)
// do things that create events ...
deleteBlock(block)
Blockly.Events.setGroup(false)The other events should be grouped together with the deletion, but they won't be because |
Current UI delete paths already check deletability. This is not reproducible through normal manual UI testing. Keep deleteBlock aligned with Blockly deletion semantics.
|
I pushed changes to address the other two reviewer feedbacks. They are not accessible through the ui, but it is possible that by not guarding against them it leaves the possibility of a bug later on in the codebase development. So even though it seems a little overly defensive I've done those changes. |
Resolves
Deleting a hat block (or any top-of-stack block) via right-click → Delete Block also deletes every block connected below it. Keyboard Delete/Backspace has the same bug. Expected: only the top block is removed; the stack below stays.
Proposed Changes
deleteBlockhelper incontext_menu_items.ts. For a top stack block (nopreviousConnectiontarget), disconnectnextConnectionbeforedisposeso the block below is left as a new top block.BlockSvg.prototype.checkAndDeletethrough the same helper inindex.tsso keyboard delete matches the context-menu path.Reason for Changes
dispose(healStack=true)on a top block has nothing to heal to, so the next block gets disposed with it. Explicitly detaching fixes both paths through one helper.Test Coverage
Two unit tests in
tests/unit/context_menu_items.test.tscovering both delete paths. Both fail without the fix.Manually verified in scratch-editor (right-click + keyboard) that deleting a hat leaves its stack intact, mid-stack delete still heals above↔below, reporter delete is unaffected, and undo restores in one step.