Repository navigation
fix(muya): correct the caret after a native word/line delete, and defer in code blocks (#5239) - #5658
Merged
Merged
Conversation
…in code blocks (#5239) #5657 let Ctrl/Alt/Cmd+Backspace reach the browser so it can delete a whole word or line. Two gaps remained. `resolveEndpoint` read the DOM offset of an element-anchored selection as a character offset. A native "delete to line start" clears the last line, leaves a trailing `<br>`, and parks the caret on the content element itself — so the surviving text `foo\n` resolved to offset 1 and the caret jumped into `foo`. Translate the child index into a text offset for that anchor. Endpoints inside a child node keep the legacy mapping, which the inline-image paths compensate for. `CodeBlockContent.backspaceHandler` owns its own token-boundary fix-up and never called `super`, so at a Prism token boundary it still intercepted the chord and trimmed one character (`const|` -> `cons x = 1` instead of ` x = 1`). Defer to the browser there too, after the offset-0 branch that converts the block to a paragraph.
Jocs
force-pushed
the
fix/5239-followup
branch
from
October 8, 2026 11:05
0b26d7b to
5b1d473
Compare
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The implementation addresses both reported regressions with focused unit and browser-level coverage.
0 open findings
What changed in this PR
Fixes native modifier-assisted Backspace behavior and caret resolution in Muya.
Changes:
- Converts element child indexes into text offsets.
- Defers modified Backspace handling in code blocks to the browser.
- Adds unit and end-to-end regression coverage.
| File | Description |
|---|---|
packages/muya/src/selection/dom.ts |
Corrects element-anchored caret offsets. |
packages/muya/src/selection/__tests__/lineAtPoint5389.spec.ts |
Uses a realistic text-node caret anchor. |
packages/muya/src/selection/__tests__/elementAnchorEndpoint.spec.ts |
Tests element and text-node endpoint resolution. |
packages/muya/src/block/content/codeBlockContent/index.ts |
Defers modified Backspace operations. |
packages/muya/e2e/tests/editing/cmd-backspace-multiline-caret-5239.spec.ts |
Covers multiline and Prism token-boundary deletion. |
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
2 of 6 tasks
Jocs
added a commit
that referenced
this pull request
Oct 8, 2026
The find bar seeds its query from the editor selection. #5658 made resolveEndpoint translate an element-anchored range's offset into a text offset (the fix for #5239), so the whole-paragraph selection the e2e helper installs now reads back as the whole paragraph instead of a single character. That seeded query matches the paragraph once, so the counter already showed "1 / 1" and the test's poll returned before the debounced search for the typed query ran; Escape then restored the cursor onto the seeded whole-paragraph match instead of the typed one. Wait for the highlight to be the typed query before pressing Escape.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #5657 (merged), which let Ctrl/Alt/Cmd+Backspace reach the browser so it can delete a word or a line.
Summary
That change left two gaps.
1. The caret lands mid-text after a native delete. A native "delete to line start" clears the last line, leaves a trailing
<br>and parks the caret on the content element itself.resolveEndpointread that element's child index as a character offset, so the survivingfoo\nresolved to offset 1 and the caret jumped intofoo. Fix: translate the child index into a text offset when the anchor is the content element. Endpoints inside a child node keep the legacy mapping, which the inline-image paths already compensate for.2. Code blocks still trimmed one character.
CodeBlockContent.backspaceHandlerowns its own Prism token-boundary fix-up and never calledsuper, so at a token boundary it intercepted the chord and removed a single grapheme —const|producedcons x = 1instead ofx = 1. Fix: defer to the browser there too, after the offset-0 branch that converts the block to a paragraph.Type of change
Test plan
elementAnchorEndpoint.spec.ts; thelineAtPoint5389.spec.tscode-block stub corrected from an impossible (content element, character offset) anchor to a realistic (text node, offset) one. Full muya suite: 283 files / 2443 tests pass.cmd-backspace-multiline-caret-5239.spec.tsdrives a realMeta/Ctrl+Backspace on a multi-line soft-break paragraph and on a highlighted code block; it asserts the caret stays at the end of the surviving text and that a Prism token boundary deletes the whole word. Both fail without these fixes.eslintandtsc --noEmitclean.Notes for reviewers
Alt+Backspace(delete-word, i.e. what Ctrl+Backspace maps to on Linux/Windows) triggers it too whenever the deletion empties the last line. fix: #5239 let Ctrl+Backspace delete a word at the end of a line #5657 was manually tested on Linux with a single-line case, which is why it slipped through.resolveEndpointonly reinterprets the content-element anchor. The nested-element case is deliberately left alone — the inline-image caret paths (_caretOffsetOnInlineImage, theParagraphContentoffset-0 branch) depend on the current value.Refs #5239, #5657