fix(convert): coalesce a bold/italic span Confluence's editor split around a link - #112
Merged
Merged
Conversation
…round a link Confluence's ADF stores marks per text run rather than as nested elements, so a bold span containing a link -- which MdToConfluence always publishes nested -- comes back from a page that's been opened and saved in Confluence's editor as two adjacent runs sharing the mark instead: <strong>text </strong><a><strong>link</strong></a>. Rendered independently that produces "**text **[**link**](url)", whose closing ** is preceded by a space and so doesn't open emphasis at all under CommonMark's flanking rule -- the exported markdown came back with literal asterisks, not just unstyled text. Verified live 2026-08-30 by PUTting a page's own unmodified atlas_doc_format body back at it (what the editor does on every save, without changing content) and confirming the storage came back split. Fixes #111.
Code review on the original fix found the same editor-induced split also happens for an internal Confluence page/space link (<ac:link>), not just a markdown link (<a>): verified live the same way, by PUTting a bold internal link's own unmodified atlas_doc_format body back at the page. A naive name check wouldn't have caught it, since ac:link's visible text sits one level deeper, inside ac:link-body, rather than directly on the link element. Also fixes a latent data-loss path the review flagged: merging two adjacent same-tag mark elements kept only one side's attrs map, silently dropping the other's.
Member
Author
|
Code review found two real gaps in the original fix, both addressed in 1831df2:
The other two findings (case duplication between the mark-before-link/mark-after-link branches, and the unconditional same-tag merge being broader than strictly needed) were judged acceptable as-is: the duplication is a few lines and adding an abstraction over it would cost more clarity than it buys, and the unconditional merge is necessary for a third adjacent run to fold into an already-repaired node and is documented as such. |
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.
Summary
MdToConfluencealways publishes a bold/italic span containing a link nested (<strong>text <a>link</a></strong>), which round-trips fine on its own — but once that page is opened and saved in Confluence's editor (even with no actual edit), it re-serializes through ADF and comes back split:<strong>text </strong><a><strong>link</strong></a>.storage_to_md.gorendered those two nodes independently, producing**text **[**link**](url). The closing**is preceded by a space, so CommonMark's flanking rule refuses to treat it as emphasis at all — the exported markdown came back with literal**characters, not just missing bold styling.atlas_doc_formatbody back at it via the API — exactly what the editor does on every save — and confirming the storage came back split.Fixes #111.
Fix
coalesceSplitMarksinstorage_to_md.go: detects adjacent inline nodes sharing a formatting mark (including a link whose entire content carries that mark) and hoists the mark to wrap the whole run before rendering — the only shape markdown can actually express.CLAUDE.md's architecture notes to describe the repair.Test plan
TestStorageToMarkdownCoalescesSplitMarks(bold/italic, both orderings, plain adjacent-run merging, partially-marked link left alone)make checkpasses (vet, fmt-check, test, build, lint)atlas_doc_formatPUT mimicking an editor save