Treat lists with a single empty item as plain text, not Markdown. (#6833)
* 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. * Fix type errors. * Lint. --------- Co-authored-by: Michael Weimann <michaelw@matrix.org> Co-authored-by: Johannes Marbach <johannesm@element.io>
This commit is contained in:
parent
33ec7147d6
commit
5aefd4652a
2 changed files with 45 additions and 1 deletions
|
@ -112,6 +112,10 @@ const innerNodeLiteral = (node: commonmark.Node): string => {
|
||||||
return literal;
|
return literal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const emptyItemWithNoSiblings = (node: commonmark.Node): boolean => {
|
||||||
|
return !node.prev && !node.next && !node.firstChild;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that wraps commonmark, adding the ability to see whether
|
* Class that wraps commonmark, adding the ability to see whether
|
||||||
* a given message actually uses any markdown syntax or whether
|
* a given message actually uses any markdown syntax or whether
|
||||||
|
@ -242,13 +246,30 @@ export default class Markdown {
|
||||||
|
|
||||||
public isPlainText(): boolean {
|
public isPlainText(): boolean {
|
||||||
const walker = this.parsed.walker();
|
const walker = this.parsed.walker();
|
||||||
|
|
||||||
let ev: commonmark.NodeWalkingStep | null;
|
let ev: commonmark.NodeWalkingStep | null;
|
||||||
|
|
||||||
while ((ev = walker.next())) {
|
while ((ev = walker.next())) {
|
||||||
const node = ev.node;
|
const node = ev.node;
|
||||||
|
|
||||||
if (TEXT_NODES.indexOf(node.type) > -1) {
|
if (TEXT_NODES.indexOf(node.type) > -1) {
|
||||||
// definitely text
|
// definitely text
|
||||||
continue;
|
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" && node.firstChild && emptyItemWithNoSiblings(node.firstChild)) {
|
||||||
|
// A list with a single empty item is treated as plain text.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type == "item" && emptyItemWithNoSiblings(node)) {
|
||||||
|
// An empty list item with no sibling items is treated as plain text.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything else is actual lists and therefore not plaintext.
|
||||||
|
return false;
|
||||||
} else if (node.type == "html_inline" || node.type == "html_block") {
|
} else if (node.type == "html_inline" || node.type == "html_block") {
|
||||||
// if it's an allowed html tag, we need to render it and therefore
|
// 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
|
// we will need to use HTML. If it's not allowed, it's not HTML since
|
||||||
|
|
|
@ -81,6 +81,29 @@ describe("editor/serialize", function () {
|
||||||
const html = htmlSerializeIfNeeded(model, {});
|
const html = htmlSerializeIfNeeded(model, {});
|
||||||
expect(html).toBe("*hello* world < hey world!");
|
expect(html).toBe("*hello* world < hey world!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("lists with a single empty item are not considered markdown", function () {
|
||||||
|
const pc = createPartCreator();
|
||||||
|
|
||||||
|
const model1 = new EditorModel([pc.plain("-")], pc);
|
||||||
|
const html1 = htmlSerializeIfNeeded(model1, {});
|
||||||
|
expect(html1).toBe(undefined);
|
||||||
|
|
||||||
|
const model2 = new EditorModel([pc.plain("* ")], pc);
|
||||||
|
const html2 = htmlSerializeIfNeeded(model2, {});
|
||||||
|
expect(html2).toBe(undefined);
|
||||||
|
|
||||||
|
const model3 = new EditorModel([pc.plain("2021.")], pc);
|
||||||
|
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")], pc);
|
||||||
|
const html = htmlSerializeIfNeeded(model, {});
|
||||||
|
expect(html).toBe('<ol start="2021">\n<li>foo</li>\n</ol>\n');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with plaintext", function () {
|
describe("with plaintext", function () {
|
||||||
|
|
Loading…
Reference in a new issue