Repository navigation
fix(replace): cut untracked spans in place across nested wrappers (#16) - #21
Conversation
An untracked replace whose match crossed a revision wrapper split it into two halves that both kept the wrapper's w:id, put the replacement between them as plain original text, and never descended a hyperlink nested inside, so the link's part of the match survived and the text scrambled. Untracked, the cross-boundary walker now recurses into an additive <w:ins>/<w:moveTo> and cuts it in place (nested hyperlinks/fields/revisions sliced at the right offsets, replacement where the match starts, no id duplicated); a subtractive wrapper stays whole. Tracked, the post-half of a split wrapper gets a fresh id from the allocator. rebuildContainer now starts at the container's own offset and counts nested wrappers, so a match in a later run of a multi-run ins or hyperlink cuts the right words. Triage-Routine: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XWuv9YGyEx2uZMPbg5Egd
…sertion (#22) The #16 in-place cut already maps offsets past another author's nested <w:del>; this regression test fails on main (it cut "The Client may " instead of "disputed amount") and passes on this branch. Triage-Routine: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKadSq4CY1JScFMncHeY4W
|
🤖 docx-cli triage This PR also fixes #22, reported by @Dmitry-Kov. In that issue, an untracked replace matches text that comes after another author's Repro: run
What I changed: I pushed one test-only commit, Action needed: approve/merge — review this PR's open decisions, then merge it to close both #16 and #22. Generated by Claude Code |
Triage-Routine: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UKadSq4CY1JScFMncHeY4W
🤖 docx-cli triage
What was wrong
docx replace inslink.docx "withhold a" "keep a"(untracked; the match starts in Reviewer A's<w:ins>and ends inside a<w:hyperlink>nested in it) printedreplace 1 occurrence replacedand exited 0. The result readThe Client may [a disputed](#x)keep aamount., and both halves of the split ins hadw:id="1". The same offset miscount also hit any untracked match in a later run of a multi-run ins or hyperlink:replace "disputed" "contested"gavemacontestedld a disputed amount..What changed
<w:ins>/<w:moveTo>is cut in place by running the same walker over its content, so nested hyperlinks, fields and revisions are cut at the right offsets. The replacement lands where the match starts, so it stays in A's insertion, and no id is duplicated. A wrapper left with no children is dropped. A subtractive<w:del>/<w:moveFrom>stays whole.w:idfrom the allocator.rebuildContainernow starts counting at the container's own offset (RunSlot.parentStart) and counts nested wrappers in any container.src/core/find/replace-span.tsx,src/core/track-changes/CLAUDE.md(updated the known-gap notes), andtests/cli/replace.test.ts(8 tests; 7 of them fail on main).Verification
The Client may keep a[ disputed](#x) amount.. The XML is<w:ins w:id="1">may · keep a · <w:hyperlink> disputed</w:hyperlink> · amount.</w:ins>, with one id.<w:hyperlink>directly inside<w:ins>) already failsdocx validatebefore any edit, because the schema only allows<w:ins>inside<w:hyperlink>. The tests for that shape skip the schema check. The other cases do validate.<w:del>nested in the ins: main cutThe Client may→Fees are due. disputed sumwithhold disputed amount.; nowFees are due. The Client may withhold disputed sum.). Its regression test was added in a follow-up commit and fails on main.bun run check✅bun run test:unit✅ (1761 tests)placedReplacementin the hyperlink branch. /code-review xhigh: the forked review ran in the main checkout, not this branch, so it reviewed merged PRs Any write turns numeric character references (•,–) into literal text #12–fix(replace): tracked span inside another author's insertion splits it Word-style #20 instead of this diff. I reviewed this diff by hand and added 2 regression tests based on its offset findings.Decisions for you
--track) match crossing a hyperlink nested inside another author's ins still cuts at the wrong offsets. Also, a tracked replace across a top-level hyperlink deletes the link's cut text without a<w:del>(read --baselineloses it). Should these be follow-up issues?Action needed: approve/merge — review the two decisions above, then merge.
Fixes #16
Fixes #22
🤖 Generated with Claude Code
https://claude.ai/code/session_019XWuv9YGyEx2uZMPbg5Egd
Generated by Claude Code
Generated by Claude Code