Rich text editor rewrites adjacent references, intraword emphasis and quoted label references on save

Summary

Saving a description, comment or wiki page from the rich text editor rewrites three kinds of markdown, with no edit made:

  1. A space is inserted between a reference and the punctuation before it: #1/#2 is saved as #1/ #2, #1-#2 as #1- #2, @root/@user as @root/ @user; the rendered text shows the stray space.
  2. Emphasis inside a word is written with underscores: a*b*c (italic b) is saved as a_b_c, which GitLab Flavored Markdown renders as literal text, so the emphasis is lost.
  3. Quoted label and milestone references are written as HTML entities: ~"UX Paper Cuts" is saved as ~"UX Paper Cuts" and %"17.5" as %"17.5". They still render, but the source is unreadable in the plain text editor, in diffs and in the description history.

Steps to reproduce

  1. In a project with two issues, a label whose name contains a space (for example UX Paper Cuts) and a milestone (for example Sprint 1), set an issue description in the plain text editor to:

    Either #1/#2 fixes it.
    
    a*b*c
    
    Tracked under ~"UX Paper Cuts" for %"Sprint 1".
  2. Select Edit title and description, switch to rich text editing if the form opens in plain text, and select Save changes without editing.

  3. Observe the rendered description: Either #1/ #2 fixes it. (stray space) and a_b_c as literal text.

  4. Open the plain text editor: the description now reads Either #1/ #2 fixes it., a_b_c, and Tracked under ~"UX Paper Cuts" for %"Sprint 1".

Example Project

Any project on GitLab.com. Reproduced on 2026-09-08 against the production markdown renderer (POST /api/v4/markdown, project gitlab-org/gitlab) fed through the editor's own deserializer and serializer.

What is the current bug behavior?

The three rewrites above happen on every save from the rich text editor, whether or not the text was edited.

What is the expected correct behavior?

Saving without edits leaves the markdown byte-identical: #1/#2, a*b*c, ~"UX Paper Cuts", %"Sprint 1".

Relevant logs and/or screenshots

Production HTML for ~"UX Paper Cuts" carries the reference as HTML-escaped text: data-original="~"UX Paper Cuts"". Deserializing then serializing #1/#2 with the editor's services gives #1/ #2; a*b*c gives a_b_c; ~"UX Paper Cuts" gives ~"UX Paper Cuts".

Output of checks

This bug happens on GitLab.com.

Possible fixes

  • Adjacent references: app/assets/javascripts/content_editor/services/serialization_helpers.js, ensureSpace writes a space before every reference unless the output ends in whitespace, a bracket, a quote or a mark delimiter; /, -, +, =, | and & are not in the list. The markdown parser only needs the space after a letter, digit or underscore (which it reads as a project path, abc#1), so the rule should be inverted to that case. Introduced by Fix reference serialization in RTE (!165017).
  • Intraword emphasis: app/assets/javascripts/content_editor/services/serializer/italic.js always writes _; CommonMark does not let _ open or close emphasis next to a letter or digit. Write * when the emphasis touches a word character on either side. Introduced by Preserve bold, italic, code, and link syntax in the Content Editor (!87157).
  • Quoted references: app/assets/javascripts/content_editor/extensions/reference.js stores dataset.original without decoding it, while Banzai::Filter::References::ReferenceFilter#data_attributes_for stores data-original as HTML-escaped text. Decode when parsing, and escape < and > when the reference is written back so a label name with angle brackets is not parsed as an HTML tag. Present since Render references in content editor (!68230).
  • Two further shapes are covered by the same MR: a path-like word typed directly before a reference chip (foo- then #1, v1. then #1) and a word typed directly after a chip (#1abc, ~bugx, @root.x) both lose the reference on render, so the spacing rule writes the protecting space on both sides (foo- #1, #1 abc) unless the neighbour is punctuation that follows another reference (#1-#2 stays tight).

Merge request: Fix reference spacing, intraword emphasis and quoted references on rich text editor save (!254251).

Fix: !254251