Markdown: Split up render function into toHTML/toPlaintext

Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
pull/21833/head
Johannes Löthberg 2017-01-18 19:29:11 +01:00
parent 6d2e521421
commit 30bd01cdf2
3 changed files with 61 additions and 47 deletions

View File

@ -23,7 +23,9 @@ import commonmark from 'commonmark';
*/ */
export default class Markdown { export default class Markdown {
constructor(input) { constructor(input) {
this.input = input this.input = input;
this.parser = new commonmark.Parser();
this.renderer = new commonmark.HtmlRenderer({safe: false});
} }
isPlainText() { isPlainText() {
@ -57,54 +59,66 @@ export default class Markdown {
return is_plain; return is_plain;
} }
render(html) { toHTML(html) {
const parser = new commonmark.Parser(); const real_paragraph = this.renderer.paragraph;
const renderer = new commonmark.HtmlRenderer({safe: true}); this.renderer.paragraph = function(node, entering) {
const real_paragraph = renderer.paragraph; // If there is only one top level node, just return the
if (html) { // bare text: it's a single line of text and so should be
renderer.paragraph = function(node, entering) { // 'inline', rather than unnecessarily wrapped in its own
// If there is only one top level node, just return the // p tag. If, however, we have multiple nodes, each gets
// bare text: it's a single line of text and so should be // its own p tag to keep them as separate paragraphs.
// 'inline', rather than unnecessarily wrapped in its own var par = node;
// p tag. If, however, we have multiple nodes, each gets while (par.parent) {
// its own p tag to keep them as separate paragraphs. par = par.parent
var par = node;
while (par.parent) {
par = par.parent
}
if (par.firstChild != par.lastChild) {
real_paragraph.call(this, node, entering);
}
} }
} else { if (par.firstChild != par.lastChild) {
// The default `out` function only sends the input through an XML real_paragraph.call(this, node, entering);
// escaping function, which causes messages to be entity encoded,
// which we don't want in this case.
renderer.out = function(s) {
this.lit(s);
} }
}
renderer.paragraph = function(node, entering) { var parsed = this.parser.parse(this.input);
// If there is only one top level node, just return the var rendered = this.renderer.render(parsed);
// bare text: it's a single line of text and so should be
// 'inline', rather than unnecessarily wrapped in its own this.renderer.paragraph = real_paragraph;
// p tag. If, however, we have multiple nodes, each gets
// its own p tag to keep them as separate paragraphs. return rendered;
var par = node; }
while (par.parent) {
node = par; toPlaintext() {
par = par.parent; const real_paragraph = this.renderer.paragraph;
}
if (node != par.lastChild) { // The default `out` function only sends the input through an XML
if (!entering) { // escaping function, which causes messages to be entity encoded,
this.lit('\n\n'); // which we don't want in this case.
} this.renderer.out = function(s) {
// The `lit` function adds a string literal to the output buffer.
this.lit(s);
}
this.renderer.paragraph = function(node, entering) {
// If there is only one top level node, just return the
// bare text: it's a single line of text and so should be
// 'inline', rather than unnecessarily wrapped in its own
// p tag. If, however, we have multiple nodes, each gets
// its own p tag to keep them as separate paragraphs.
var par = node;
while (par.parent) {
node = par;
par = par.parent;
}
if (node != par.lastChild) {
if (!entering) {
this.lit('\n\n');
} }
} }
} }
var parsed = parser.parse(this.input); var parsed = this.parser.parse(this.input);
return renderer.render(parsed); var rendered = this.renderer.render(parsed);
this.renderer.paragraph = real_paragraph;
return rendered;
} }
} }

View File

@ -401,7 +401,7 @@ export default class MessageComposerInput extends React.Component {
let contentState = null; let contentState = null;
if (enabled) { if (enabled) {
const md = new Markdown(this.state.editorState.getCurrentContent().getPlainText()); const md = new Markdown(this.state.editorState.getCurrentContent().getPlainText());
contentState = RichText.HTMLtoContentState(md.render(true)); contentState = RichText.HTMLtoContentState(md.toHTML());
} else { } else {
let markdown = stateToMarkdown(this.state.editorState.getCurrentContent()); let markdown = stateToMarkdown(this.state.editorState.getCurrentContent());
if (markdown[markdown.length - 1] === '\n') { if (markdown[markdown.length - 1] === '\n') {
@ -524,9 +524,9 @@ export default class MessageComposerInput extends React.Component {
} else { } else {
const md = new Markdown(contentText); const md = new Markdown(contentText);
if (md.isPlainText()) { if (md.isPlainText()) {
contentText = md.render(false); contentText = md.toPlaintext();
} else { } else {
contentHTML = md.render(true); contentHTML = md.toHTML(true);
} }
} }

View File

@ -325,13 +325,13 @@ module.exports = React.createClass({
} }
if (send_markdown) { if (send_markdown) {
const htmlText = mdown.render(true); const htmlText = mdown.toHTML();
sendMessagePromise = isEmote ? sendMessagePromise = isEmote ?
MatrixClientPeg.get().sendHtmlEmote(this.props.room.roomId, contentText, htmlText) : MatrixClientPeg.get().sendHtmlEmote(this.props.room.roomId, contentText, htmlText) :
MatrixClientPeg.get().sendHtmlMessage(this.props.room.roomId, contentText, htmlText); MatrixClientPeg.get().sendHtmlMessage(this.props.room.roomId, contentText, htmlText);
} }
else { else {
const contentText = mdown.render(false); const contentText = mdown.toPlaintext(false);
sendMessagePromise = isEmote ? sendMessagePromise = isEmote ?
MatrixClientPeg.get().sendEmoteMessage(this.props.room.roomId, contentText) : MatrixClientPeg.get().sendEmoteMessage(this.props.room.roomId, contentText) :
MatrixClientPeg.get().sendTextMessage(this.props.room.roomId, contentText); MatrixClientPeg.get().sendTextMessage(this.props.room.roomId, contentText);