diff --git a/packages/app-cli/tests/html_to_md/linebreaks.md b/packages/app-cli/tests/html_to_md/linebreaks.md index 116e9d1cb..21edaaadb 100644 --- a/packages/app-cli/tests/html_to_md/linebreaks.md +++ b/packages/app-cli/tests/html_to_md/linebreaks.md @@ -15,7 +15,4 @@ however. Because it isn't necessary. - - - -... \ No newline at end of file +


... \ No newline at end of file diff --git a/packages/app-cli/tests/html_to_md/repeated_brs.html b/packages/app-cli/tests/html_to_md/repeated_brs.html new file mode 100644 index 000000000..9d8e90668 --- /dev/null +++ b/packages/app-cli/tests/html_to_md/repeated_brs.html @@ -0,0 +1,4 @@ +A


test.
+ +A single <br/>
can use two spaces at the end of the line, +but

the markdown renderer discards these if the line is otherwise empty. \ No newline at end of file diff --git a/packages/app-cli/tests/html_to_md/repeated_brs.md b/packages/app-cli/tests/html_to_md/repeated_brs.md new file mode 100644 index 000000000..6d425697f --- /dev/null +++ b/packages/app-cli/tests/html_to_md/repeated_brs.md @@ -0,0 +1,5 @@ +A +

test. +A single <br/> +can use two spaces at the end of the line, but +
the markdown renderer discards these if the line is otherwise empty. \ No newline at end of file diff --git a/packages/turndown/src/commonmark-rules.js b/packages/turndown/src/commonmark-rules.js index 190948d81..8ff223d5d 100644 --- a/packages/turndown/src/commonmark-rules.js +++ b/packages/turndown/src/commonmark-rules.js @@ -45,11 +45,18 @@ rules.paragraph = { rules.lineBreak = { filter: 'br', - replacement: function (content, node, options) { + replacement: function (_content, node, options, previousNode) { + let brReplacement = options.br + '\n'; + // Code blocks may include
s -- replacing them should not be necessary // in code blocks. - const brReplacement = node.isCode ? '' : options.br; - return brReplacement + '\n' + if (node.isCode) { + brReplacement = '\n'; + } else if (previousNode && previousNode.nodeName === 'BR') { + brReplacement = '
'; + } + + return brReplacement; } } diff --git a/packages/turndown/src/turndown.js b/packages/turndown/src/turndown.js index 1d38839d3..71fb54dfa 100644 --- a/packages/turndown/src/turndown.js +++ b/packages/turndown/src/turndown.js @@ -165,27 +165,32 @@ TurndownService.prototype = { function process (parentNode, escapeContent = 'auto') { if (this.options.disableEscapeContent) escapeContent = false; - var self = this - return reduce.call(parentNode.childNodes, function (output, node) { - node = new Node(node, self.options) + let output = ''; + let previousNode = null; + + for (let node of parentNode.childNodes) { + node = new Node(node, this.options); var replacement = '' if (node.nodeType === 3) { if (node.isCode || escapeContent === false) { replacement = node.nodeValue } else { - replacement = self.escape(node.nodeValue) + replacement = this.escape(node.nodeValue); // Escape < and > so that, for example, this kind of HTML text: "This is a tag: <p>" is still rendered as "This is a tag: <p>" // and not "This is a tag:

". If the latter, it means the HTML will be rendered if the viewer supports HTML (which, in Joplin, it does). replacement = replacement.replace(/<(.+?)>/g, '<$1>'); } } else if (node.nodeType === 1) { - replacement = replacementForNode.call(self, node) + replacement = replacementForNode.call(this, node, previousNode); } - return join(output, replacement) - }, '') + output = join(output, replacement); + previousNode = node; + } + + return output; } /** @@ -211,11 +216,12 @@ function postProcess (output) { * Converts an element node to its Markdown equivalent * @private * @param {HTMLElement} node The node to convert + * @param {HTMLElement|null} previousNode The node immediately before this node. * @returns A Markdown representation of the node * @type String */ -function replacementForNode (node) { +function replacementForNode (node, previousNode) { var rule = this.rules.forNode(node) var content = process.call(this, node, rule.escapeContent ? rule.escapeContent(node) : 'auto') var whitespace = node.flankingWhitespace @@ -223,7 +229,7 @@ function replacementForNode (node) { return ( whitespace.leading + - rule.replacement(content, node, this.options) + + rule.replacement(content, node, this.options, previousNode) + whitespace.trailing ) }