diff --git a/src/editor/deserialize.js b/src/editor/deserialize.js index d41e413dbc..6636c9971e 100644 --- a/src/editor/deserialize.js +++ b/src/editor/deserialize.js @@ -58,7 +58,16 @@ function parseLink(a, partCreator) { function parseCodeBlock(n, partCreator) { const parts = []; - const preLines = ("```\n" + n.textContent + "```").split("\n"); + let language = ""; + if (n.firstChild && n.firstChild.nodeName === "CODE") { + for (const className of n.firstChild.classList) { + if (className.startsWith("language-")) { + language = className.substr("language-".length); + break; + } + } + } + const preLines = ("```" + language + "\n" + n.textContent + "```").split("\n"); preLines.forEach((l, i) => { parts.push(partCreator.plain(l)); if (i < preLines.length - 1) { @@ -99,7 +108,10 @@ function parseElement(n, partCreator, lastNode, state) { case "LI": { const indent = " ".repeat(state.listDepth - 1); if (n.parentElement.nodeName === "OL") { - return partCreator.plain(`${indent}1. `); + // The markdown parser doesn't do nested indexed lists at all, but this supports it anyway. + let index = state.listIndex[state.listIndex.length - 1]; + state.listIndex[state.listIndex.length - 1] += 1; + return partCreator.plain(`${indent}${index}. `); } else { return partCreator.plain(`${indent}- `); } @@ -111,23 +123,25 @@ function parseElement(n, partCreator, lastNode, state) { break; } case "OL": + state.listIndex.push(n.start || 1); + // fallthrough case "UL": state.listDepth = (state.listDepth || 0) + 1; - // es-lint-disable-next-line no-fallthrough + // fallthrough default: - // don't textify block nodes we'll decend into - if (!checkDecendInto(n)) { + // don't textify block nodes we'll descend into + if (!checkDescendInto(n)) { return partCreator.plain(n.textContent); } } } -function checkDecendInto(node) { +function checkDescendInto(node) { switch (node.nodeName) { case "PRE": // a code block is textified in parseCodeBlock // as we don't want to preserve markup in it, - // so no need to decend into it + // so no need to descend into it return false; default: return checkBlockNode(node); @@ -168,7 +182,9 @@ function parseHtmlMessage(html, partCreator, isQuotedMessage) { const parts = []; let lastNode; let inQuote = isQuotedMessage; - const state = {}; + const state = { + listIndex: [], + }; function onNodeEnter(n) { if (checkIgnored(n)) { @@ -203,11 +219,11 @@ function parseHtmlMessage(html, partCreator, isQuotedMessage) { parts.push(...newParts); - const decend = checkDecendInto(n); - // when not decending (like for PRE), onNodeLeave won't be called to set lastNode + const descend = checkDescendInto(n); + // when not descending (like for PRE), onNodeLeave won't be called to set lastNode // so do that here. - lastNode = decend ? null : n; - return decend; + lastNode = descend ? null : n; + return descend; } function onNodeLeave(n) { @@ -219,6 +235,8 @@ function parseHtmlMessage(html, partCreator, isQuotedMessage) { inQuote = false; break; case "OL": + state.listIndex.pop(); + // fallthrough case "UL": state.listDepth -= 1; break; diff --git a/src/editor/dom.js b/src/editor/dom.js index e82c3f70ca..3efc64f1c9 100644 --- a/src/editor/dom.js +++ b/src/editor/dom.js @@ -21,8 +21,8 @@ import DocumentOffset from "./offset"; export function walkDOMDepthFirst(rootNode, enterNodeCallback, leaveNodeCallback) { let node = rootNode.firstChild; while (node && node !== rootNode) { - const shouldDecend = enterNodeCallback(node); - if (shouldDecend && node.firstChild) { + const shouldDescend = enterNodeCallback(node); + if (shouldDescend && node.firstChild) { node = node.firstChild; } else if (node.nextSibling) { node = node.nextSibling;