Skip to content

fix(react): prevent intra-word _ and * from rendering as emphasis - #1375

Open
rahulnits-sketch wants to merge 1 commit into
RocketChat:developfrom
rahulnits-sketch:fix/intra-word-emphasis
Open

rahulnits-sketch wants to merge 1 commit into
RocketChat:developfrom
rahulnits-sketch:fix/intra-word-emphasis

Conversation

@rahulnits-sketch

@rahulnits-sketch rahulnits-sketch commented Sep 23, 2026 •

Copy link
Copy Markdown

Brief Title

fix(react): prevent intra-word _ and * from rendering as emphasis in composer

Acceptance Criteria fulfillment

  • Prevent intra-word _ and * delimiters from being parsed as italics or bold (e.g. my_file_name.js, foo_bar_baz)
  • Ensure delimiters touching whitespace (e.g. 2 * 3 * 4) do not trigger emphasis
  • Retain expected markdown behavior for valid bold/italic spans (e.g. _italic_, **bold**, (_italic_))
  • All 10 workspace packages build successfully via yarn build

Fixes #1372

PR Test Details

Note: The PR will be ready for live testing at https://rocketchat.github.io/EmbeddedChat/pulls/pr-1375 after approval.

Test Matrix

Input Before (Bug) After (Fixed)
my_file_name.js my<em>file</em>name.js my_file_name.js
call foo_bar_baz() now call foo<em>bar</em>baz() now call foo_bar_baz() now
2 * 3 * 4 = 24 2 <em> 3 </em> 4 = 24 2 * 3 * 4 = 24
This is _italic_ text This is <em>italic</em> text This is <em>italic</em> text
This is **bold** text This is <strong>bold</strong> text This is <strong>bold</strong> text
(_italic_) (<em>italic</em>) (<em>italic</em>)

@CLAassistant

CLAassistant commented Sep 23, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@rahulnits-sketch

Copy link
Copy Markdown
Author

Hi @Spiral-Memory,

I have updated the commit author to align with my signed CLA, so all checks (including the CLA assistant) are now passing green ✅.

The changes in contentEditableComposer.js have been tested against all intra-word edge cases and all 10 workspace packages compile cleanly via yarn build. Whenever you have a moment, I would appreciate your review and feedback. Thank you!

@ishan-one8 ishan-one8 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rahulnits-sketch — fair warning before anything else: I filed #1372 and I have an overlapping PR open (#1376), so I'm not a neutral reviewer here. I'd rather be useful than quiet about it, so I checked your branch out and actually ran it instead of just reading the diff.

The three cases from the issue are genuinely fixed, and _italic_, **bold**, __init__ and (_italic_) all still work. I ran your branch and develop through the reference commonmark package side by side, and two cases come out worse than before this change:

Input develop today this PR commonmark
**bold**text <strong>bold</strong>text *<em>bold</em>*text <strong>bold</strong>text
foo*bar*baz foo<em>bar</em>baz foo*bar*baz foo<em>bar</em>baz

The first one looks like a real regression — the output gains stray asterisks and drops from <strong> to <em>. I left line comments on where I think each comes from.

My actual question (not rhetorical — I couldn't work it out from the diff): what is the isAlphanumeric(nextChar) check on line 62 for? CommonMark's closing rule cares about whitespace before the closer, and only _ cares about a word character after it. I'm guessing you hit a specific input that needed it and I can't reconstruct which one — if you tell me, I suspect it changes what the right fix is.

Nothing here is blocking from me; I'm not a maintainer. Just passing on what the comparison turned up.


if (isWhitespace(firstInside) || isWhitespace(lastInside)) return false;
if (isAlphanumeric(prevChar)) return false;
if (isAlphanumeric(nextChar)) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is what breaks **bold**text.

For that input the ** run is rejected here (nextChar is t), so the loop falls back one character and re-matches a single * instead. That single * does pass the checks, so you end up emitting <em>bold</em> plus two leftover literal asterisks: *<em>bold</em>*text. On develop the same input renders correctly as <strong>bold</strong>text, so this is a regression rather than a pre-existing gap.

The related one: this check also rejects intra-word *, which CommonMark actually allows — foo*bar*baz is foo<em>bar</em>baz in the reference implementation, and on develop today, but renders literally with this PR.

Scoping the word-character rule to _ only would fix both — _ is the one CommonMark forbids inside a word, * is deliberately allowed.

@@ -92,7 +112,10 @@ const appendMarkdown = (parent, text) => {
if (token) {
const [marker, tagName] = token;
const end = text.indexOf(marker, index + marker.length);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, and separate from the regression above: end is resolved once here, so when the nearest closer fails isValidEmphasis the whole run is abandoned even if a valid closer exists further along.

*foo * bar* is the case — commonmark gives <em>foo * bar</em>, this PR leaves it literal. Continuing the scan (indexOf(marker, end + marker.length) in a loop until one passes) handles it. Worth a follow-up rather than something I'd hold this PR for.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: AI composer renders intra-word _ and * as italic/bold (e.g. my_file_name.js -> my<em>file</em>name.js)

3 participants