From 2d45c56ba53fc582b55ed844a2f9fa54737d85a6 Mon Sep 17 00:00:00 2001
From: Robin Ward
Date: Mon, 26 Aug 2013 15:21:23 -0400
Subject: [PATCH] Replace Markdown Linebreak Regexp with node parser.
---
.../discourse/components/markdown.js | 10 -----
.../discourse/dialects/bbcode_dialect.js | 2 +-
.../javascripts/discourse/dialects/dialect.js | 18 +++++---
.../discourse/dialects/github_code_dialect.js | 42 ++++++++++++++++++-
.../discourse/dialects/newline_dialect.js | 42 +++++++++++++++++++
spec/components/pretty_text_spec.rb | 6 +--
test/javascripts/components/bbcode_test.js | 2 +-
test/javascripts/components/markdown_test.js | 21 ++++++----
8 files changed, 111 insertions(+), 32 deletions(-)
create mode 100644 app/assets/javascripts/discourse/dialects/newline_dialect.js
diff --git a/app/assets/javascripts/discourse/components/markdown.js b/app/assets/javascripts/discourse/components/markdown.js
index 57fb2e0dd43..1f1c1929ba6 100644
--- a/app/assets/javascripts/discourse/components/markdown.js
+++ b/app/assets/javascripts/discourse/components/markdown.js
@@ -97,17 +97,7 @@ Discourse.Markdown = {
return {
makeHtml: function(text) {
- // Linebreaks
- var linebreaks = opts.traditional_markdown_linebreaks || Discourse.SiteSettings.traditional_markdown_linebreaks;
- if (!linebreaks) {
- text = text.replace(/(^[\w<][^\n]*\n+)/gim, function(t) {
- if (t.match(/\n{2}/gim)) return t;
- return t.replace("\n", " \n");
- });
- }
-
text = Discourse.Dialect.cook(text, opts);
-
if (!text) return "";
if (opts.sanitize) {
diff --git a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js
index 9ac4475af1a..8f2c9fb3071 100644
--- a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js
+++ b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js
@@ -174,7 +174,7 @@ Discourse.Dialect.on("register", function(event) {
result.push(['p', ['aside', params,
['div', {'class': 'title'},
['div', {'class': 'quote-controls'}],
- avatarImg ? avatarImg + "\n" : "",
+ avatarImg ? avatarImg : "",
I18n.t('user.said',{username: username})
],
contents
diff --git a/app/assets/javascripts/discourse/dialects/dialect.js b/app/assets/javascripts/discourse/dialects/dialect.js
index 9a5b6ce976e..6d2d50e535c 100644
--- a/app/assets/javascripts/discourse/dialects/dialect.js
+++ b/app/assets/javascripts/discourse/dialects/dialect.js
@@ -32,8 +32,7 @@
```javascript
Discourse.Dialect.on("parseNode", function(event) {
- var node = event.node,
- path = event.path;
+ var node = event.node;
if (node[0] === 'code') {
node[node.length-1] = "EVIL TROUT HACKED YOUR CODE";
@@ -68,16 +67,22 @@ var parser = window.BetterMarkdown,
@method parseTree
@param {Array} tree the JsonML tree to parse
@param {Array} path the path of ancestors to the current node in the tree. Can be used for matching.
+ @param {Object} insideCounts counts what tags we're inside
@returns {Array} the parsed tree
**/
- parseTree = function parseTree(tree, path) {
+ parseTree = function parseTree(tree, path, insideCounts) {
if (tree instanceof Array) {
- Discourse.Dialect.trigger('parseNode', {node: tree, path: path});
+ Discourse.Dialect.trigger('parseNode', {node: tree, path: path, dialect: dialect, insideCounts: insideCounts || {}});
path = path || [];
+ insideCounts = insideCounts || {};
+
path.push(tree);
tree.slice(1).forEach(function (n) {
- parseTree(n, path);
+ var tagName = n[0];
+ insideCounts[tagName] = (insideCounts[tagName] || 0) + 1;
+ parseTree(n, path, insideCounts);
+ insideCounts[tagName] = insideCounts[tagName] - 1;
});
path.pop();
}
@@ -103,7 +108,8 @@ Discourse.Dialect = {
cook: function(text, opts) {
if (!initialized) { initializeDialects(); }
dialect.options = opts;
- return parser.renderJsonML(parseTree(parser.toHTMLTree(text, 'Discourse')));
+ var tree = parser.toHTMLTree(text, 'Discourse');
+ return parser.renderJsonML(parseTree(tree));
}
};
diff --git a/app/assets/javascripts/discourse/dialects/github_code_dialect.js b/app/assets/javascripts/discourse/dialects/github_code_dialect.js
index f1a717ef9d2..c83d21d68ed 100644
--- a/app/assets/javascripts/discourse/dialects/github_code_dialect.js
+++ b/app/assets/javascripts/discourse/dialects/github_code_dialect.js
@@ -84,10 +84,48 @@ Discourse.Dialect.on("register", function(event) {
@namespace Discourse.Dialect
**/
Discourse.Dialect.on("parseNode", function(event) {
- var node = event.node,
- path = event.path;
+ var node = event.node;
if (node[0] === 'code') {
node[node.length-1] = Handlebars.Utils.escapeExpression(node[node.length-1]);
}
});
+
+
+Discourse.Dialect.on("parseNode", function(event) {
+
+ var node = event.node,
+ opts = event.dialect.options,
+ insideCounts = event.insideCounts,
+ linebreaks = opts.traditional_markdown_linebreaks || Discourse.SiteSettings.traditional_markdown_linebreaks;
+
+ if (!linebreaks) {
+ // We don't add line breaks inside a pre
+ if (insideCounts.pre > 0) { return; }
+
+ if (node.length > 1) {
+ for (var j=1; j 0) {
+ spliceInstructions.push(split[i]);
+ if (i !== split.length-1) { spliceInstructions.push(['br']); }
+ }
+ }
+ node.splice.apply(node, spliceInstructions);
+ }
+ }
+ }
+ }
+ }
+ }
+});
diff --git a/app/assets/javascripts/discourse/dialects/newline_dialect.js b/app/assets/javascripts/discourse/dialects/newline_dialect.js
new file mode 100644
index 00000000000..5859a3ed5df
--- /dev/null
+++ b/app/assets/javascripts/discourse/dialects/newline_dialect.js
@@ -0,0 +1,42 @@
+/**
+ Support for the newline behavior in markdown that most expect.
+
+ @event parseNode
+ @namespace Discourse.Dialect
+**/
+Discourse.Dialect.on("parseNode", function(event) {
+ var node = event.node,
+ opts = event.dialect.options,
+ insideCounts = event.insideCounts,
+ linebreaks = opts.traditional_markdown_linebreaks || Discourse.SiteSettings.traditional_markdown_linebreaks;
+
+ if (!linebreaks) {
+ // We don't add line breaks inside a pre
+ if (insideCounts.pre > 0) { return; }
+
+ if (node.length > 1) {
+ for (var j=1; j 0) {
+ spliceInstructions.push(split[i]);
+ if (i !== split.length-1) { spliceInstructions.push(['br']); }
+ }
+ }
+ node.splice.apply(node, spliceInstructions);
+ }
+ }
+ }
+ }
+ }
+ }
+});
\ No newline at end of file
diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb
index b41ba19bc77..82ee603d18e 100644
--- a/spec/components/pretty_text_spec.rb
+++ b/spec/components/pretty_text_spec.rb
@@ -14,15 +14,15 @@ describe PrettyText do
end
it "produces a quote even with new lines in it" do
- PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd\n[/quote]").should match_html "\n
\n
\nEvilTrout said:
\nddd\n
"
+ PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd\n[/quote]").should match_html "\n
\n
EvilTrout said:
\nddd \n
"
end
it "should produce a quote" do
- PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd[/quote]").should match_html "\n
\n
\nEvilTrout said:
\nddd
"
+ PrettyText.cook("[quote=\"EvilTrout, post:123, topic:456, full:true\"]ddd[/quote]").should match_html "\n
\n
EvilTrout said:
\nddd
"
end
it "trims spaces on quote params" do
- PrettyText.cook("[quote=\"EvilTrout, post:555, topic: 666\"]ddd[/quote]").should match_html "\n
\n
\nEvilTrout said:
\nddd
"
+ PrettyText.cook("[quote=\"EvilTrout, post:555, topic: 666\"]ddd[/quote]").should match_html "\n
\n
EvilTrout said:
\nddd
"
end
end
diff --git a/test/javascripts/components/bbcode_test.js b/test/javascripts/components/bbcode_test.js
index d198ffd92e2..08a93df0246 100644
--- a/test/javascripts/components/bbcode_test.js
+++ b/test/javascripts/components/bbcode_test.js
@@ -88,7 +88,7 @@ test("quote formatting", function() {
format("[quote=\"eviltrout, post:1, topic:1\"]abc[/quote]\nhello",
"
\n\n\nhello",
+ "
abc \n\nhello",
"handles new lines properly");
});
diff --git a/test/javascripts/components/markdown_test.js b/test/javascripts/components/markdown_test.js
index e0840812ffc..eb9457e569b 100644
--- a/test/javascripts/components/markdown_test.js
+++ b/test/javascripts/components/markdown_test.js
@@ -20,8 +20,7 @@ test("basic cooking", function() {
cooked("***hello***", "
hello
", "it can do bold and italics at once.");
});
-test("Line Breaks", function() {
-
+test("Traditional Line Breaks", function() {
var input = "1\n2\n3";
cooked(input, "1 2 3
", "automatically handles trivial newlines");
@@ -34,7 +33,12 @@ test("Line Breaks", function() {
Discourse.SiteSettings.traditional_markdown_linebreaks = true;
cooked(input, traditionalOutput, "It supports traditional markdown via a Site Setting");
+});
+test("Line Breaks", function() {
+ cooked("[] first choice\n[] second choice",
+ "[] first choice [] second choice
",
+ "it handles new lines correctly with [] options");
});
test("Links", function() {
@@ -79,11 +83,10 @@ test("Links", function() {
"Here's a tweet:https://twitter.com/evil_trout/status/345954894420787200
",
"It doesn't strip the new line.");
- cooked("1. View @eviltrout's profile here: http://meta.discourse.org/users/eviltrout/activity\nnext line.",
+ cooked("1. View @eviltrout's profile here: http://meta.discourse.org/users/eviltrout/activity next line.",
"View @eviltrout 's profile here: http://meta.discourse.org/users/eviltrout/activity next line. ",
"allows autolinking within a list without inserting a paragraph.");
-
cooked("[3]: http://eviltrout.com", "", "It doesn't autolink markdown link references");
cooked("http://discourse.org and http://discourse.org/another_url and http://www.imdb.com/name/nm2225369",
@@ -98,13 +101,13 @@ test("Quotes", function() {
cookedOptions("[quote=\"eviltrout, post: 1\"]\na quote\n\nsecond line\n[/quote]",
{ topicId: 2 },
- "\n" +
+ "
",
"works with multiple lines");
cookedOptions("1[quote=\"bob, post:1\"]my quote[/quote]2",
{ topicId: 2, lookupAvatar: function(name) { return "" + name; } },
- "1
\n\n
bob\n" +
+ "
1
\n\n
\n\n
2
",
"handles quotes properly");
@@ -138,7 +141,7 @@ test("Mentions", function() {
"handles mentions in simple quotes");
cooked("> foo bar baz @eviltrout ohmagerd\nlook at this",
- "
foo bar baz @eviltrout ohmagerd\nlook at this
",
+ "
foo bar baz @eviltrout ohmagerd look at this
",
"does mentions properly with trailing text within a simple quote");
cooked("`code` is okay before @mention",
@@ -162,7 +165,7 @@ test("Mentions", function() {
"you can have a mention in an inline code block following a real mention.");
cooked("1. this is a list\n\n2. this is an @eviltrout mention\n",
- "
this is a list
this is an @eviltrout mention
",
+ "
this is a list
this is an @eviltrout mention
",
"it mentions properly in a list.");
cookedOptions("@eviltrout", alwaysTrue,
@@ -202,7 +205,7 @@ test("Code Blocks", function() {
"it supports basic code blocks");
cooked("```json\n{hello: 'world'}\n```\ntrailing",
- "
{hello: 'world'}
\n\n
\ntrailing
",
+ "
{hello: 'world'}
\n\n
trailing
",
"It does not truncate text after a code block.");
cooked("```json\nline 1\n\nline 2\n\n\nline3\n```",