diff --git a/src/Markdown.js b/src/Markdown.js index 785aa4abfd..3a9cad6401 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -16,33 +16,9 @@ limitations under the License. import marked from 'marked'; -// replace the default link renderer function -// to prevent marked from turning plain URLs -// into links, because tits algorithm is fairly -// poor, so let's send plain URLs rather than -// badly linkified ones (the linkifier Vector -// uses on message display is way better, eg. -// handles URLs with closing parens at the end). -const renderer = new marked.Renderer(); -renderer.link = function(href, title, text) { - if (text == href) { - return href; - } - return marked.Renderer.prototype.apply(this, arguments); -} -const PARAGRAPH_SUFFIX = '

'; -// suffix paragraphs with double line breaks instead of -// wrapping them in 'p' tags: this makes it much easier -// for us to just strip one set of these off at the end, -// leaving valid markup if there were multiple paragraphs. -renderer.paragraph = function(text) { - return text + PARAGRAPH_SUFFIX; -} - // marked only applies the default options on the high // level marked() interface, so we do it here. const marked_options = Object.assign({}, marked.defaults, { - renderer: renderer, gfm: true, tables: true, breaks: true, @@ -53,8 +29,6 @@ const marked_options = Object.assign({}, marked.defaults, { xhtml: true, // return self closing tags (ie.
not
) }); -const real_parser = new marked.Parser(marked_options); - /** * Class that wraps marked, adding the ability to see whether * a given message actually uses any markdown syntax or whether @@ -90,36 +64,65 @@ export default class Markdown { is_plain = false; } - const dummyRenderer = {}; + const dummy_renderer = {}; for (const k of Object.keys(marked.Renderer.prototype)) { - dummyRenderer[k] = setNotPlain; + dummy_renderer[k] = setNotPlain; } // text and paragraph are just text - dummyRenderer.text = function(t){return t;} - dummyRenderer.paragraph = function(t){return t;} + dummy_renderer.text = function(t){return t;} + dummy_renderer.paragraph = function(t){return t;} // ignore links where text is just the url: // this ignores plain URLs that markdown has // detected whilst preserving markdown syntax links - dummyRenderer.link = function(href, title, text) { + dummy_renderer.link = function(href, title, text) { if (text != href) { is_plain = false; } } - const dummyOptions = {}; - Object.assign(dummyOptions, marked_options, { - renderer: dummyRenderer, + const dummy_options = Object.assign({}, marked_options, { + renderer: dummy_renderer, }); - const dummyParser = new marked.Parser(dummyOptions); - dummyParser.parse(this._copyTokens()); + const dummy_parser = new marked.Parser(dummy_options); + dummy_parser.parse(this._copyTokens()); return is_plain; } toHTML() { - return real_parser.parse(this._copyTokens()).slice( - 0, 0 - PARAGRAPH_SUFFIX.length - ); + const real_renderer = new marked.Renderer(); + real_renderer.link = function(href, title, text) { + // prevent marked from turning plain URLs + // into links, because tits algorithm is fairly + // poor. Let's send plain URLs rather than + // badly linkified ones (the linkifier Vector + // uses on message display is way better, eg. + // handles URLs with closing parens at the end). + if (text == href) { + return href; + } + return marked.Renderer.prototype.apply(this, arguments); + } + + real_renderer.paragraph = (text) => { + // The tokens at the top level are the 'blocks', so if we + // have more than one, there are multiple 'paragraphs'. + // If there is only one top level token, just return the + // bare text: it's a single line of text and so should be + // 'inline', rather than necessarily wrapped in its own + // p tag. If, however, we have multiple tokens, each gets + // its own p tag to keep them as separate paragraphs. + if (this.tokens.length == 1) { + return text; + } + return '

' + text + '

'; + } + + const real_options = Object.assign({}, marked_options, { + renderer: real_renderer, + }); + const real_parser = new marked.Parser(real_options); + return real_parser.parse(this._copyTokens()); } }