diff --git a/packages/react/src/lib/contentEditableComposer.js b/packages/react/src/lib/contentEditableComposer.js index ffa3f6f9c..03e568aa1 100644 --- a/packages/react/src/lib/contentEditableComposer.js +++ b/packages/react/src/lib/contentEditableComposer.js @@ -44,6 +44,30 @@ const findLink = (text, start) => { }; }; +const isAlphanumeric = (ch) => Boolean(ch && /[a-zA-Z0-9]/.test(ch)); +const isWhitespace = (ch) => Boolean(ch && /\s/.test(ch)); + +const isValidEmphasis = (marker, text, start, end) => { + if (marker === '`' || marker === '~~') { + return true; + } + const prevChar = start > 0 ? text[start - 1] : ''; + const firstInside = text[start + marker.length]; + const lastInside = text[end - 1]; + const nextChar = + end + marker.length < text.length ? text[end + marker.length] : ''; + + if (isWhitespace(firstInside) || isWhitespace(lastInside)) return false; + + // CommonMark: only underscore delimiters cannot be intra-word + if (marker === '_' || marker === '__') { + if (isAlphanumeric(prevChar)) return false; + if (isAlphanumeric(nextChar)) return false; + } + + return true; +}; + const appendMarkdown = (parent, text) => { const tokens = [ ['**', 'strong'], @@ -91,7 +115,10 @@ const appendMarkdown = (parent, text) => { if (token) { const [marker, tagName] = token; - const end = text.indexOf(marker, index + marker.length); + let end = text.indexOf(marker, index + marker.length); + while (end !== -1 && !isValidEmphasis(marker, text, index, end)) { + end = text.indexOf(marker, end + marker.length); + } if (end > index + marker.length) { flush(); const formatted = document.createElement(tagName);