diff --git a/crates/wysiwyg/src/dom/parser/markdown/markdown_html_parser.rs b/crates/wysiwyg/src/dom/parser/markdown/markdown_html_parser.rs index 9a8c8f49d..f0c8ce23c 100644 --- a/crates/wysiwyg/src/dom/parser/markdown/markdown_html_parser.rs +++ b/crates/wysiwyg/src/dom/parser/markdown/markdown_html_parser.rs @@ -60,9 +60,15 @@ impl MarkdownHTMLParser { .replace("
\n", "
") .replace("
\n", "
") .replace("
\n", "
") + .replace("
\n", "
")
+            .replace("
\n", "
") .replace("

\n", "

") .replace("

\n", "

"); + // Remove the newline from the end of the single code tag that wraps the content + // of a formatted codeblock + let html = html.replace("\n", ""); + Ok(S::try_from(html).unwrap()) } } diff --git a/crates/wysiwyg/src/tests/test_set_content.rs b/crates/wysiwyg/src/tests/test_set_content.rs index 92d6cf6dc..9c2b046ea 100644 --- a/crates/wysiwyg/src/tests/test_set_content.rs +++ b/crates/wysiwyg/src/tests/test_set_content.rs @@ -93,3 +93,12 @@ fn set_content_from_markdown_blockquote_multiline() { "

quote

following text|

" ); } + +#[test] +fn set_content_from_markdown_codeblock_with_newlines() { + let mut model = cm("|"); + model + .set_content_from_markdown(&utf16("```\nI am a code block\n```")) + .unwrap(); + assert_eq!(tx(&model), "
I am a code block|
"); +} diff --git a/platforms/web/lib/conversion.test.ts b/platforms/web/lib/conversion.test.ts index c5dd2a290..8a98f89ca 100644 --- a/platforms/web/lib/conversion.test.ts +++ b/platforms/web/lib/conversion.test.ts @@ -80,6 +80,30 @@ describe('Rich text <=> plain text', () => { expect(convertedRichText).toBe(expectedRichText); }); + + it('converts code spans from plain => rich', async () => { + const plainText = '```I am a code span```'; + const convertedRichText = await plainToRich(plainText, false); + const expectedRichText = 'I am a code span'; + + expect(convertedRichText).toBe(expectedRichText); + }); + + it('converts code blocks from plain => rich with newline separation', async () => { + const plainText = '```\nI am a code block\n```'; + const convertedRichText = await plainToRich(plainText, false); + const expectedRichText = '
I am a code block
'; + + expect(convertedRichText).toBe(expectedRichText); + }); + + it('converts code blocks from plain => rich with div separation', async () => { + const plainText = '```
I codeblock
```
'; + const convertedRichText = await plainToRich(plainText, false); + const expectedRichText = '
I codeblock
'; + + expect(convertedRichText).toBe(expectedRichText); + }); }); describe('markdownToPlain', () => { @@ -92,11 +116,9 @@ describe('markdownToPlain', () => { }); it('converts multiple linebreak for markdown => plain', () => { - // nb for correct display, there will be one \n more - // than \\\n at the end const markdown = 'multiple\\\nline\\\n\\\nbreaks\\\n\\\n\\\n'; const convertedPlainText = markdownToPlain(markdown); - const expectedPlainText = 'multiple\nline\n\nbreaks\n\n\n\n'; + const expectedPlainText = 'multiple\nline\n\nbreaks\n\n'; expect(convertedPlainText).toBe(expectedPlainText); }); diff --git a/platforms/web/lib/conversion.ts b/platforms/web/lib/conversion.ts index 408afd2a6..09e23c157 100644 --- a/platforms/web/lib/conversion.ts +++ b/platforms/web/lib/conversion.ts @@ -24,14 +24,14 @@ import { initOnce } from './useComposerModel.js'; const NEWLINE_CHAR = '\n'; // In plain text, markdown newlines (displays '\' character followed by -// a newline character) will be represented as \n for display as the -// display box can not interpret markdown. -// We also may need to manually add a \n to account for trailing newlines. +// a newline character) will be represented as \n for setting in the composer. +// If we have a trailing newline character, we trim it off so that the cursor +// will always be at the last character of the string, not on a new line in the +// composer. export const markdownToPlain = (markdown: string) => { let plainText = markdown; if (plainText.endsWith(NEWLINE_CHAR)) { - // for cursor positioning we need to manually add another linebreak - plainText = `${plainText}${NEWLINE_CHAR}`; + plainText = plainText.slice(0, -1); } return plainText.replaceAll(/\\/g, ''); };