Skip to content

Fix deleteText removing all inline children from a block#158

Merged
hackerwins merged 1 commit into
mainfrom
fix/delete-text-empty-inline-guard
Apr 24, 2026
Merged

Fix deleteText removing all inline children from a block#158
hackerwins merged 1 commit into
mainfrom
fix/delete-text-empty-inline-guard

Conversation

@hackerwins

@hackerwins hackerwins commented Apr 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fix stale array guard in deleteText cleanup loop that allowed removing all inline children from a block, leaving it in an invalid state
  • The inlines snapshot array was never updated after each removal, so the length guard always checked the original count
  • Apply the same splice pattern already used in applyStyle to keep the array in sync with the tree

Root Cause

When a block has 2+ empty inlines after text deletion, the cleanup loop removes them one by one. The guard if (inlines.length <= 1) break checked the snapshot array length, which never decreased. With 2 empty inlines, both were removed, leaving the block with zero inline children. Subsequent editByPath calls on that block triggered YorkieError: unacceptable path.

Confirmed by replaying all 451 changes from the production document (server_seq=630).

Test plan

  • New test: block with 2 empty inlines → insert text → delete text → verify exactly 1 inline remains
  • pnpm verify:fast passes (200 tests, 0 failures)
  • pnpm verify:self passes (full build + tests)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed text deletion handling when blocks contain multiple empty inline nodes to prevent unintended removal of required inline children.
  • Tests

    • Added test coverage for text deletion scenarios with empty inline nodes.

The empty-inline cleanup loop in deleteText used a snapshot array
to guard against removing the last inline, but never updated the
array after each removal. With 2+ empty inlines the stale length
check allowed all of them to be deleted, leaving the block with
zero inline children and causing "unacceptable path" errors on
subsequent edits.

Apply the same splice pattern already used in applyStyle so the
guard tracks the actual count.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Updated the deleteText cleanup logic to correctly handle empty inline node removal during backward iteration. The fix modifies the loop condition to prevent removing all inline children and explicitly splices entries from the local inlines list to maintain consistent state during iteration.

Changes

Cohort / File(s) Summary
deleteText Cleanup Logic
packages/frontend/src/app/docs/yorkie-doc-store.ts
Modified loop condition to prevent deleting below one inline and added explicit splice calls to track array shrinkage during backward iteration through empty inlines.
deleteText Edge Case Test
packages/frontend/tests/app/docs/yorkie-doc-store.test.ts
Added test case verifying that deleting all text from a block with multiple empty inline nodes preserves exactly one inline child.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A loop that spirals back with care,
One inline always lingers there,
No longer does the array shrink too fast,
With splice and splice, the state shall last!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and concisely describes the main fix: preventing deleteText from removing all inline children from a block, which is the core issue addressed in this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/delete-text-empty-inline-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/frontend/tests/app/docs/yorkie-doc-store.test.ts (1)

494-546: Good regression test — covers the exact production failure shape.

The test faithfully reproduces the scenario (block with 2 empty inlines after split-fragment state) and asserts both the tree invariant (exactly 1 inline child) and that getDocument() still round-trips cleanly — which was the symptom path that ultimately raised YorkieError: unacceptable path.

One small optional refinement: consider also asserting the inline has text === '' after delete, so a future regression that accidentally preserved the wrong inline (e.g. left 'X' behind) would be caught too. Not a blocker.

Optional additional assertion
       assert.equal(result.blocks[1].inlines.length, 1);
+      assert.equal(result.blocks[1].inlines[0].text, '');
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/frontend/tests/app/docs/yorkie-doc-store.test.ts` around lines 494 -
546, Add an extra assertion after the deleteText step to verify the remaining
inline's text is empty: locate the test "should keep at least one inline when
deleting all text from a block with multiple empty inlines" and after computing
inlinesAfter (from blockAfter.children) assert that inlinesAfter[0].text === ''
(or the equivalent property used by your model) before calling
store.getDocument(); this will ensure the preserved inline contains an empty
string rather than leftover content from the deleted text (references:
makeBlock, store.splitBlock, doc.update, store.insertText, store.deleteText,
store.getDocument).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/frontend/tests/app/docs/yorkie-doc-store.test.ts`:
- Around line 494-546: Add an extra assertion after the deleteText step to
verify the remaining inline's text is empty: locate the test "should keep at
least one inline when deleting all text from a block with multiple empty
inlines" and after computing inlinesAfter (from blockAfter.children) assert that
inlinesAfter[0].text === '' (or the equivalent property used by your model)
before calling store.getDocument(); this will ensure the preserved inline
contains an empty string rather than leftover content from the deleted text
(references: makeBlock, store.splitBlock, doc.update, store.insertText,
store.deleteText, store.getDocument).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 39ce5981-15fe-44ea-b4d3-47e28661e76e

📥 Commits

Reviewing files that changed from the base of the PR and between 6192257 and 05b1d73.

📒 Files selected for processing (2)
  • packages/frontend/src/app/docs/yorkie-doc-store.ts
  • packages/frontend/tests/app/docs/yorkie-doc-store.test.ts

@github-actions

github-actions Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 116.7s

Lane Status Duration
sheets:build ✅ pass 12.8s
docs:build ✅ pass 7.8s
verify:fast ✅ pass 58.7s
frontend:build ✅ pass 15.8s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.7s
cli:build ✅ pass 1.7s
verify:entropy ✅ pass 14.9s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented Apr 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@hackerwins
hackerwins merged commit ea7b90b into main Apr 24, 2026
4 checks passed
@hackerwins
hackerwins deleted the fix/delete-text-empty-inline-guard branch April 24, 2026 00:18
@hackerwins hackerwins mentioned this pull request May 1, 2026
3 tasks
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.

1 participant