Keep formatting marks in one span around links in the rich text editor
What does this MR do and why?
A bold, italic or strikethrough span that wraps a link is split into separate marked segments around the link on the next rich text save. **see [docs](https://docs.gitlab.com) now** becomes **see** [**docs**](https://docs.gitlab.com) **now**. Any edit anywhere in the document triggers it, because the editor re-serializes the whole document on change; a save with no edit is safe.
Bold and italic still render after the split, so the damage surfaces in the plain text editor, in diffs and in description history. Strikethrough visibly loses its line through the spaces around the link, and a link followed by inline code inside a bold span saves as [**docs **](url), which renders literal asterisks inside the link text.
Root cause: prosemirror-markdown reorders a text node's marks to match the marks already open, but only moves marks whose serializer sets mixable: true, and it stops at the first mark without the flag. Every other inline mark sets it; the link serializer never did. Tiptap's Link extension has priority 1000, so the link sorts first and the reorder stops there, closing the open bold before the link and reopening it inside.
This MR sets mixable: true on the link serializer, as upstream prosemirror-markdown does for its own link mark.
Unchanged: a link with no other marks, marks inside a link ([**docs**](url)), autolinks, titled links, upload links, reference and wiki links, and inline code next to a link. A bold span that begins with a link still serializes as [**docs**](url) **now**, because marks carry no nesting order in the document and the serializer opens them in schema order. That output is valid and renders identically.
Resolves #514245
Follow-up, not changed here: #628260
Evidence
- Round trip over 33 fixtures rendered by the production markdown API (bold, italic, both, strikethrough, two links in one span, link at span end, inline code adjacent, bare URL, titled link, upload link, and a bold link inside a nested list item, table cell, blockquote, heading and task item): byte-identical with this change, split at the merge base. Fixtures with no mark wrapping a link are byte-identical on both.
- Jest: 12 new examples in
serializer/link_spec.jsfail at the merge base and pass here; 2 more pin the unchanged cases. Spec 25/25; wholecontent_editortree 1212/1212 under both Vue 2 and Vue 3. - Corpus check on 55,755 open
gitlab-org/gitlabissue descriptions (2026-09-09): 1,761 carry a mark wrapping a link, which is the shape this protects on the next rich text edit. - Screenshots and recordings below: 12 matrix rows and 3 GIF stories.
How the flag was lost
The editor's first link serializer carried mixable: true, like upstream's. Allow rendering block content in tables (!66187) rewrote the link block in 2021 and dropped the flag (commit 9034aab3ca53); the later rewrites in Preserve bold, italic, code, and link syntax in the Content Editor (!87157) and Split markdown serializer for marks in separate files (!159630) kept that shape. Bold Text Rendering Bug (#523066) is the same defect, closed as a duplicate.
Screenshots or screen recordings
Each row seeds the description, opens it in rich text, types " (edited)" on the plain first line, saves, then shows the description rendered or its saved markdown reopened in the plain text editor. Row 5 follows the report's own steps instead (select the line with the link, apply bold). Rows 10 to 12 are byte-identical between the sides.
Recordings
Four beats per side: rendered, editor with the edit typed, rendered after save, saved markdown. Beats before the save are byte-identical; the first story diverges at the rendered beat, the second only at the source beat, the third never.
How to set up and validate locally
- Set an issue description in the plain text editor to
**see [docs](https://docs.gitlab.com) now**and save. - Edit it, switch to rich text, type a word on any line, and save.
- Reopen the description in the plain text editor. Before:
**see** [**docs**](https://docs.gitlab.com) **now**. After: the span is intact. - Repeat with
_read the [guide](url) first_and~~old [link](url) text~~(strikethrough visibly breaks before the fix). Also try a bold span holding a link followed by an inline code span: before the fix the rendered link text shows literal asterisks. - In rich text, type
see docs now, linkdocs, select the line, press Cmd+B and save: the markdown stays**see [docs](url) now**. - Regressions:
[**docs**](url), a plain link,**bold**and a link in one paragraph, and a bold link in a list item or table cell all round-trip unchanged.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.





























