[RFC][lexical-markdown] Replace whitespace with code point when the string has leading and trailing whitespaces (#7400)

This commit is contained in:
Yuncheng Lu
2025-03-29 23:38:42 +08:00
committed by GitHub
parent dfeb7c79fc
commit 4d459e3f47
3 changed files with 34 additions and 8 deletions

View File

@ -203,10 +203,13 @@ function exportTextFormat(
): string {
// This function handles the case of a string looking like this: " foo "
// Where it would be invalid markdown to generate: "** foo **"
// We instead want to trim the whitespace out, apply formatting, and then
// bring the whitespace back. So our returned string looks like this: " **foo** "
const frozenString = textContent.trim();
let output = frozenString;
// If the node has no format, we use the original text.
// Otherwise, we escape leading and trailing whitespaces to their corresponding code points,
// ensuring the returned string maintains its original formatting, e.g., "**   foo   **".
let output =
node.getFormat() === 0
? textContent
: escapeLeadingAndTrailingWhitespaces(textContent);
if (!node.hasFormat('code')) {
// Escape any markdown characters in the text content
@ -287,7 +290,7 @@ function exportTextFormat(
output = openingTags + output + closingTagsAfter;
// Replace trimmed version of textContent ensuring surrounding whitespace is not modified
return closingTagsBefore + textContent.replace(frozenString, () => output);
return closingTagsBefore + output;
}
// Get next or previous text sibling a text node, including cases
@ -342,3 +345,9 @@ function hasFormat(
): boolean {
return $isTextNode(node) && node.hasFormat(format);
}
function escapeLeadingAndTrailingWhitespaces(textContent: string) {
return textContent.replace(/^\s+|\s+$/g, (match) => {
return [...match].map((char) => '&#' + char.codePointAt(0) + ';').join('');
});
}

View File

@ -360,14 +360,17 @@ describe('Markdown', () => {
{
html: '<p><b><strong style="white-space: pre-wrap;">Hello </strong></b><s><b><strong style="white-space: pre-wrap;">world</strong></b></s><span style="white-space: pre-wrap;">!</span></p>',
md: '**Hello ~~world~~**!',
mdAfterExport: '**Hello&#32;~~world~~**!',
},
{
html: '<p><s><b><strong style="white-space: pre-wrap;">Hello </strong></b></s><s><i><b><strong style="white-space: pre-wrap;">world</strong></b></i></s><s><span style="white-space: pre-wrap;">!</span></s></p>',
md: '**~~Hello *world*~~**~~!~~',
mdAfterExport: '**~~Hello&#32;*world*~~**~~!~~',
},
{
html: '<p><i><em style="white-space: pre-wrap;">Hello </em></i><i><b><strong style="white-space: pre-wrap;">world</strong></b></i><i><em style="white-space: pre-wrap;">!</em></i></p>',
md: '*Hello **world**!*',
mdAfterExport: '*Hello&#32;**world**!*',
},
{
html: '<p><span style="white-space: pre-wrap;">helloworld</span></p>',
@ -548,20 +551,26 @@ describe('Markdown', () => {
{
html: '<p><span style="white-space: pre-wrap;">Text </span><b><strong style="white-space: pre-wrap;">boldstart </strong></b><a href="https://lexical.dev"><b><strong style="white-space: pre-wrap;">text</strong></b></a><b><strong style="white-space: pre-wrap;"> boldend</strong></b><span style="white-space: pre-wrap;"> text</span></p>',
md: 'Text **boldstart [text](https://lexical.dev) boldend** text',
mdAfterExport:
'Text **boldstart&#32;[text](https://lexical.dev)&#32;boldend** text',
},
{
html: '<p><span style="white-space: pre-wrap;">Text </span><b><strong style="white-space: pre-wrap;">boldstart </strong></b><a href="https://lexical.dev"><b><code spellcheck="false" style="white-space: pre-wrap;"><strong>text</strong></code></b></a><b><strong style="white-space: pre-wrap;"> boldend</strong></b><span style="white-space: pre-wrap;"> text</span></p>',
md: 'Text **boldstart [`text`](https://lexical.dev) boldend** text',
mdAfterExport:
'Text **boldstart&#32;[`text`](https://lexical.dev)&#32;boldend** text',
},
{
html: '<p><span style="white-space: pre-wrap;">It </span><s><i><b><strong style="white-space: pre-wrap;">works </strong></b></i></s><a href="https://lexical.io"><s><i><b><strong style="white-space: pre-wrap;">with links</strong></b></i></s></a><span style="white-space: pre-wrap;"> too</span></p>',
md: 'It ~~___works [with links](https://lexical.io)___~~ too',
mdAfterExport: 'It ***~~works [with links](https://lexical.io)~~*** too',
mdAfterExport:
'It ***~~works&#32;[with links](https://lexical.io)~~*** too',
},
{
html: '<p><span style="white-space: pre-wrap;">It </span><s><i><b><strong style="white-space: pre-wrap;">works </strong></b></i></s><a href="https://lexical.io"><s><i><b><strong style="white-space: pre-wrap;">with links</strong></b></i></s></a><s><i><b><strong style="white-space: pre-wrap;"> too</strong></b></i></s><span style="white-space: pre-wrap;">!</span></p>',
md: 'It ~~___works [with links](https://lexical.io) too___~~!',
mdAfterExport: 'It ***~~works [with links](https://lexical.io) too~~***!',
mdAfterExport:
'It ***~~works&#32;[with links](https://lexical.io)&#32;too~~***!',
},
{
html: '<p><a href="https://lexical.dev"><span style="white-space: pre-wrap;">link</span></a><a href="https://lexical.dev"><span style="white-space: pre-wrap;">link2</span></a></p>',
@ -609,6 +618,10 @@ describe('Markdown', () => {
html: '<p><span style="white-space: pre-wrap;">*Hello* world</span></p>',
md: '\\*Hello\\* world',
},
{
html: '<p><b><strong style="white-space: pre-wrap;">&nbsp;</strong></b></p>',
md: '**&#160;**',
},
];
const HIGHLIGHT_TEXT_MATCH_IMPORT: TextMatchTransformer = {

View File

@ -133,6 +133,10 @@ export function importTextTransformers(
// Handle escape characters
const textContent = textNode.getTextContent();
const escapedText = textContent.replace(/\\([*_`~])/g, '$1');
const escapedText = textContent
.replace(/\\([*_`~])/g, '$1')
.replace(/&#(\d+);/g, (_, codePoint) => {
return String.fromCodePoint(codePoint);
});
textNode.setTextContent(escapedText);
}