feat: render w:noBreakHyphen tags (SD-2746)#3168
feat: render w:noBreakHyphen tags (SD-2746)#3168luccas-harbour wants to merge 9 commits intomainfrom
Conversation
The atom lives inside its own run wrapper after import, so "caret right after the hyphen" resolves to one of three PM positions: inside the atom's run (nodeBefore = atom), at paragraph level between runs (nodeBefore = the wrapper run), or at the start of the next run (nodeBefore = null). Without a handler for these, the keymap chain fell through to backspaceAcrossRuns, whose findPreviousTextDeleteRange skips non-text inline nodes by design (so bookmarkEnd markers survive backspace) β it then walked past the atom and deleted the previous character instead, so the hyphen visually appeared not to delete. Adds backspaceAtomBefore, slotted before backspaceAcrossRuns in the keymap chain. It handles all three caret positions and gates on an opt-in allowlist (currently just noBreakHyphen) to preserve bookmarkEnd's existing behavior. Covered by tests/behavior/tests/basic-commands/sd-2746-no-break-hyphen- backspace.spec.ts (three caret-position scenarios).
Inline leaf atoms like noBreakHyphen previously surfaced the generic leafFallback placeholder when flattened, so search, get-text, and diff consumers couldn't see the rendered character. Surface PM's `leafText` NodeSpec field through the Schema builder, declare it on the noBreakHyphen extension (β U+2011), and honor it in textBetweenWithTabs with a fallback for leaves that don't define one.
charOffsetToDocPos previously only counted text nodes, so character offsets computed against `textBetweenWithTabs` (which emits '\t' for tabs and the leafText glyph for atoms like noBreakHyphen) drifted past those nodes and resolved to the wrong PM position. text.rewrite then landed edits at the wrong boundary β e.g. rewriting "aβb" β "aβc" replaced 'a' instead of 'b'. Mirror the same accounting here: tabs contribute one slot, inline leaves contribute `leafText(node).length`, and offsets that fall strictly inside an atom resolve to the position immediately after it. Add an integration spec covering round-tripping, the prefix-match regression, and the symmetric suffix case.
Backspace already handles every caret position around the atom, but forward delete was broken when the caret sat inside the atom's wrapper run with the atom as nodeAfter β the whole keymap chain bailed and nothing happened. Add a `deleteAtomAfter` command that mirrors `backspaceAtomBefore`'s case 1 (remove the wrapper run when the atom is its only child) and slot it into the Delete chain after `deleteSkipEmptyRun`. Rename the spec file to cover both directions and add tests for all three "before atom" caret positions.
|
The Status: PASS The Element identity β Export structure β The decoder wraps the element in Void element handling β Unicode value β U+2011 ( One non-spec note worth flagging β Several test assertions in |
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6a11255733
βΉοΈ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Codecov Reportβ All modified and coverable lines are covered by tests. π’ Thoughts on this report? Let us know! |
Summary
Adds full support for OOXML
<w:noBreakHyphen/>tags so non-breaking hyphens are preserved end-to-end (import β schema β adapter β render β export) and behave like the visible U+2011 character users expect β including in search, get-text, diffing, and the document-apitext.rewriteoperation.The atom is selectable, deletable from either side via Backspace/Delete, and round-trips back to
<w:noBreakHyphen/>on export.Changes
Schema & extension
noBreakHyphenPM node (atom, inline, non-selectable) βsuper-editor/src/editors/v1/extensions/no-break-hyphen/.Schema.jssurfaces PM's function-valuedleafTextNodeSpec field (without invoking it) so flattening APIs see the rendered glyph.Import / export
<w:noBreakHyphen/>to the new node.noBreakHyphentranslator with co-located tests (translator +r-translatorintegration).<w:noBreakHyphen/>.Layout / pm-adapter
no-break-hyphen.tsinline converter emits aTextRuncarrying U+2011 β DomPainter renders it through the existing text path with no painter changes.paragraph.tswires the converter into the run pipeline.Editing behavior
backspaceAtomBeforecommand + keymap entry: handles all three caret positions around the atom (inside its run, between runs, at start of next run) on an opt-in allowlist (currently justnoBreakHyphen) so existingbookmarkEndbehavior is preserved.deleteAtomAftercommand + keymap entry: mirrors the above for forward-delete when the caret sits inside the atom's wrapper run.backspaceAcrossRuns, which intentionally skips non-text inline nodes and would delete the previous character instead of the hyphen.Document-API / text helpers
textBetweenWithTabshonorsleafText(with a fallback for leaves that don't define one) β search/get-text/diff now see U+2011 instead of the generic placeholder.plan-engine/executor.tscharOffsetToDocPosnow counts atomic inline leaves asleafText(node).lengthslots (and tabs as one slot), sotext.rewritelands edits at the correct PM position. Previously, rewriting"aβb"β"aβc"replaced'a'instead of'b'.