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("") .replace("\n", "
\n", "") .replace("") + .replace("\n", "
\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
';
+
+ 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, '');
};