From d10464a5de2ac3f3b0df01d5474c45e79ce95748 Mon Sep 17 00:00:00 2001 From: Denis Kasak Date: Tue, 31 Aug 2021 13:37:57 +0200 Subject: [PATCH 1/3] Treat lists with a single empty item as plain text, not Markdown. This allows users to send simple messages such as `-`, `+`, `*` and `2021.` without Element Web mangling them into a nonsensical empty list. As soon as any non-whitespace content is added to the item, it switches back to treating it as a list of one item. Fixes vector-im/element-web#7631. --- src/Markdown.ts | 19 +++++++++++++++++++ test/editor/serialize-test.js | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/Markdown.ts b/src/Markdown.ts index 96169d4011d..932a39c2b7c 100644 --- a/src/Markdown.ts +++ b/src/Markdown.ts @@ -80,12 +80,30 @@ export default class Markdown { isPlainText(): boolean { const walker = this.parsed.walker(); + let emptyItemWithNoSiblings = (node: Node) => { + return !node.prev && !node.next && !node.firstChild + } + let ev; while ( (ev = walker.next()) ) { const node = ev.node; if (TEXT_NODES.indexOf(node.type) > -1) { // definitely text continue; + } else if (node.type == 'list' || node.type == 'item') { + // Special handling for inputs like `+`, `*`, `-` and `2021.` which + // would otherwise be treated as a list of a single empty item. + // See https://github.com/vector-im/element-web/issues/7631 + if (node.type == 'list' && emptyItemWithNoSiblings(node.firstChild)) { + // A list with a single empty item is treated as plain text. + continue; + } else if (node.type == 'item' && emptyItemWithNoSiblings(node)) { + // An empty list item with no sibling items is treated as plain text. + continue; + } else { + // Everything else is actual lists and therefore not plaintext. + return false; + } } else if (node.type == 'html_inline' || node.type == 'html_block') { // if it's an allowed html tag, we need to render it and therefore // we will need to use HTML. If it's not allowed, it's not HTML since @@ -97,6 +115,7 @@ export default class Markdown { return false; } } + return true; } diff --git a/test/editor/serialize-test.js b/test/editor/serialize-test.js index 085a8afdbae..9acfea59876 100644 --- a/test/editor/serialize-test.js +++ b/test/editor/serialize-test.js @@ -44,6 +44,28 @@ describe('editor/serialize', function() { const html = htmlSerializeIfNeeded(model, {}); expect(html).toBe("hello world"); }); + it('lists with a single empty item are not considered markdown', function() { + const pc = createPartCreator(); + + const model1 = new EditorModel([pc.plain("-")]); + const html1 = htmlSerializeIfNeeded(model1, {}); + expect(html1).toBe(undefined); + + const model2 = new EditorModel([pc.plain("* ")]); + const html2 = htmlSerializeIfNeeded(model2, {}); + expect(html2).toBe(undefined); + + const model3 = new EditorModel([pc.plain("2021.")]); + const html3 = htmlSerializeIfNeeded(model3, {}); + expect(html3).toBe(undefined); + + }); + it('lists with a single non-empty item are still markdown', function() { + const pc = createPartCreator(); + const model = new EditorModel([pc.plain("2021. foo")]); + const html = htmlSerializeIfNeeded(model, {}); + expect(html).toBe("
    \n
  1. foo
  2. \n
\n"); + }); it('displaynames ending in a backslash work', function() { const pc = createPartCreator(); const model = new EditorModel([pc.userPill("Displayname\\", "@user:server")]); From b2d1a37885bc16ee819025ecb8363fa9dbd3c00a Mon Sep 17 00:00:00 2001 From: Denis Kasak Date: Fri, 17 Sep 2021 20:14:31 +0200 Subject: [PATCH 2/3] Fix type errors. --- src/Markdown.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Markdown.ts b/src/Markdown.ts index 932a39c2b7c..02d61cf93f9 100644 --- a/src/Markdown.ts +++ b/src/Markdown.ts @@ -80,11 +80,11 @@ export default class Markdown { isPlainText(): boolean { const walker = this.parsed.walker(); - let emptyItemWithNoSiblings = (node: Node) => { + let emptyItemWithNoSiblings = (node: commonmark.Node) => { return !node.prev && !node.next && !node.firstChild } - let ev; + let ev: commonmark.NodeWalkingStep; while ( (ev = walker.next()) ) { const node = ev.node; if (TEXT_NODES.indexOf(node.type) > -1) { From bbec11db3b04ba08a7d838438ffd6f66205e9adf Mon Sep 17 00:00:00 2001 From: Denis Kasak Date: Fri, 17 Sep 2021 20:24:10 +0200 Subject: [PATCH 3/3] Lint. --- src/Markdown.ts | 6 +++--- test/editor/serialize-test.js | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Markdown.ts b/src/Markdown.ts index 02d61cf93f9..340a9b487ce 100644 --- a/src/Markdown.ts +++ b/src/Markdown.ts @@ -80,9 +80,9 @@ export default class Markdown { isPlainText(): boolean { const walker = this.parsed.walker(); - let emptyItemWithNoSiblings = (node: commonmark.Node) => { - return !node.prev && !node.next && !node.firstChild - } + const emptyItemWithNoSiblings = (node: commonmark.Node) => { + return !node.prev && !node.next && !node.firstChild; + }; let ev: commonmark.NodeWalkingStep; while ( (ev = walker.next()) ) { diff --git a/test/editor/serialize-test.js b/test/editor/serialize-test.js index 9acfea59876..067e8521d01 100644 --- a/test/editor/serialize-test.js +++ b/test/editor/serialize-test.js @@ -58,7 +58,6 @@ describe('editor/serialize', function() { const model3 = new EditorModel([pc.plain("2021.")]); const html3 = htmlSerializeIfNeeded(model3, {}); expect(html3).toBe(undefined); - }); it('lists with a single non-empty item are still markdown', function() { const pc = createPartCreator();