Repository navigation
Bug: A code span of only spaces grows by two spaces on every save, and loses its bold or italic #9405
Copy link
Copy link
Closed
Description
Lexical version: 0.52.0 (main at 98e936d gives the same results)
Steps to reproduce
Import each line below with $convertFromMarkdownString(markdown, TRANSFORMERS), then export it with $convertToMarkdownString(TRANSFORMERS), and repeat:
| Markdown | Imported | Exported | Exported again | Expected |
|---|---|---|---|---|
` ` |
code (1 space) ✅ |
` ` (3 spaces) |
` ` (5 spaces) |
` ` |
` ` |
code with 2 spaces ✅ | ` ` |
` ` |
` ` |
a ` ` b |
a , code , b ✅ |
a ` ` b |
a ` ` b |
unchanged |
**` `** |
code , bold ✅ |
` ` (bold lost) |
` ` |
**` `** |
*` `* x |
code , italic ✅ |
` ` x (italic lost) |
` ` x |
unchanged |
Current behaviour
- Import is correct: CommonMark strips one space from each end only when the content isn't all spaces, so
` `is a single space. Export, though, pads every code span that starts and ends with whitespace, including one that is only spaces. A reader never strips that padding, so the span gets two spaces longer on every save. - Bold, italic and other formats around inline code that holds only whitespace are dropped on export.
Cause
getCodeSpanDelimiter()inpackages/lexical-markdown/src/MarkdownTransformers.tspads when/^\s/.test(content) && /\s$/.test(content), without checking for a character other than a space.checkHasFormat()inpackages/lexical-markdown/src/MarkdownExport.tsreturnsfalsefor a node whose text is only whitespace, unless the format being checked iscode. That treats a whitespace-only code node as having no bold, italic or other format, soexportTextFormat()never opens those tags around it. Inline code is written with its backticks, so formats can go around it like any other text.
Suggested fix
--- a/packages/lexical-markdown/src/MarkdownTransformers.ts
+++ b/packages/lexical-markdown/src/MarkdownTransformers.ts
@@ -1098,7 +1098,10 @@ export function getCodeSpanDelimiter(content: string): {
const needsPadding =
content.length === 0 ||
content.includes('`') ||
- (/^\s/.test(content) && /\s$/.test(content));
+ // A reader strips one space from each end only when the content is not
+ // all spaces, so padding content of only spaces would add two spaces.
+ // https://spec.commonmark.org/0.31.2/#code-spans
+ (/^\s/.test(content) && /\s$/.test(content) && /[^ ]/.test(content));
const padded = needsPadding ? ` ${content} ` : content;
--- a/packages/lexical-markdown/src/MarkdownExport.ts
+++ b/packages/lexical-markdown/src/MarkdownExport.ts
@@ -651,7 +651,9 @@ function checkHasFormat(n: TextNode | null, f: TextFormatType): boolean {
if (!hasFormat(n, f)) {
return false;
}
- if (f === 'code') {
+ // Inline code is written with its backticks, so other formats can go around
+ // it even when it holds only whitespace.
+ if (f === 'code' || (n && n.hasFormat('code'))) {
return true;
}With this change, all the rows above round-trip unchanged. These are unchanged too: ` a ` (exported as `a`, as before), ` a `, `a` and **`a`** b.
Tested with headless Lexical against main at 98e936d. The unit tests for lexical-markdown, lexical-mdast and lexical-playground/__tests__/unit pass (1175), along with tsc, ESLint and Prettier. No new unit tests are added.
Related: PR #8723 (code spans containing backticks), which added the padding rules.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity