\n")
+ .append("
\n")
.append(parsed)
.append("\n
")
.toString();
diff --git a/zeppelin-web/src/app/interpreter/interpreter.css b/zeppelin-web/src/app/interpreter/interpreter.css
index ee0bf1e5240..6fa59a31a4d 100644
--- a/zeppelin-web/src/app/interpreter/interpreter.css
+++ b/zeppelin-web/src/app/interpreter/interpreter.css
@@ -114,7 +114,7 @@
* github flavored markdown style
*/
-.markdown {
+.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 1.5;
@@ -124,201 +124,201 @@
word-wrap: break-word;
}
-.markdown .pl-c {
+.markdown-body .pl-c {
color: #969896;
}
-.markdown .pl-c1,
-.markdown .pl-s .pl-v {
+.markdown-body .pl-c1,
+.markdown-body .pl-s .pl-v {
color: #0086b3;
}
-.markdown .pl-e,
-.markdown .pl-en {
+.markdown-body .pl-e,
+.markdown-body .pl-en {
color: #795da3;
}
-.markdown .pl-smi,
-.markdown .pl-s .pl-s1 {
+.markdown-body .pl-smi,
+.markdown-body .pl-s .pl-s1 {
color: #333;
}
-.markdown .pl-ent {
+.markdown-body .pl-ent {
color: #63a35c;
}
-.markdown .pl-k {
+.markdown-body .pl-k {
color: #a71d5d;
}
-.markdown .pl-s,
-.markdown .pl-pds,
-.markdown .pl-s .pl-pse .pl-s1,
-.markdown .pl-sr,
-.markdown .pl-sr .pl-cce,
-.markdown .pl-sr .pl-sre,
-.markdown .pl-sr .pl-sra {
+.markdown-body .pl-s,
+.markdown-body .pl-pds,
+.markdown-body .pl-s .pl-pse .pl-s1,
+.markdown-body .pl-sr,
+.markdown-body .pl-sr .pl-cce,
+.markdown-body .pl-sr .pl-sre,
+.markdown-body .pl-sr .pl-sra {
color: #183691;
}
-.markdown .pl-v {
+.markdown-body .pl-v {
color: #ed6a43;
}
-.markdown .pl-id {
+.markdown-body .pl-id {
color: #b52a1d;
}
-.markdown .pl-ii {
+.markdown-body .pl-ii {
color: #f8f8f8;
background-color: #b52a1d;
}
-.markdown .pl-sr .pl-cce {
+.markdown-body .pl-sr .pl-cce {
font-weight: bold;
color: #63a35c;
}
-.markdown .pl-ml {
+.markdown-body .pl-ml {
color: #693a17;
}
-.markdown .pl-mh,
-.markdown .pl-mh .pl-en,
-.markdown .pl-ms {
+.markdown-body .pl-mh,
+.markdown-body .pl-mh .pl-en,
+.markdown-body .pl-ms {
font-weight: bold;
color: #1d3e81;
}
-.markdown .pl-mq {
+.markdown-body .pl-mq {
color: #008080;
}
-.markdown .pl-mi {
+.markdown-body .pl-mi {
font-style: italic;
color: #333;
}
-.markdown .pl-mb {
+.markdown-body .pl-mb {
font-weight: bold;
color: #333;
}
-.markdown .pl-md {
+.markdown-body .pl-md {
color: #bd2c00;
background-color: #ffecec;
}
-.markdown .pl-mi1 {
+.markdown-body .pl-mi1 {
color: #55a532;
background-color: #eaffea;
}
-.markdown .pl-mdr {
+.markdown-body .pl-mdr {
font-weight: bold;
color: #795da3;
}
-.markdown .pl-mo {
+.markdown-body .pl-mo {
color: #1d3e81;
}
-.markdown .octicon {
+.markdown-body .octicon {
display: inline-block;
vertical-align: text-top;
fill: currentColor;
}
-.markdown a {
+.markdown-body a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
-.markdown a:active,
-.markdown a:hover {
+.markdown-body a:active,
+.markdown-body a:hover {
outline-width: 0;
}
-.markdown strong {
+.markdown-body strong {
font-weight: inherit;
}
-.markdown strong {
+.markdown-body strong {
font-weight: bolder;
}
-.markdown h1 {
+.markdown-body h1 {
font-size: 2em;
margin: 0.67em 0;
}
-.markdown img {
+.markdown-body img {
border-style: none;
}
-.markdown svg:not(:root) {
+.markdown-body svg:not(:root) {
overflow: hidden;
}
-.markdown code,
-.markdown kbd,
-.markdown pre {
+.markdown-body code,
+.markdown-body kbd,
+.markdown-body pre {
font-family: monospace, monospace;
font-size: 1em;
}
-.markdown hr {
+.markdown-body hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
-.markdown input {
+.markdown-body input {
font: inherit;
margin: 0;
}
-.markdown input {
+.markdown-body input {
overflow: visible;
}
-.markdown button:-moz-focusring,
-.markdown [type="button"]:-moz-focusring,
-.markdown [type="reset"]:-moz-focusring,
-.markdown [type="submit"]:-moz-focusring {
+.markdown-body button:-moz-focusring,
+.markdown-body [type="button"]:-moz-focusring,
+.markdown-body [type="reset"]:-moz-focusring,
+.markdown-body [type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
-.markdown [type="checkbox"] {
+.markdown-body [type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
-.markdown * {
+.markdown-body * {
box-sizing: border-box;
}
-.markdown input {
+.markdown-body input {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
-.markdown a {
+.markdown-body a {
color: #4078c0;
text-decoration: none;
}
-.markdown a:hover,
-.markdown a:active {
+.markdown-body a:hover,
+.markdown-body a:active {
text-decoration: underline;
}
-.markdown strong {
+.markdown-body strong {
font-weight: 600;
}
-.markdown hr {
+.markdown-body hr {
height: 0;
margin: 15px 0;
overflow: hidden;
@@ -327,170 +327,170 @@
border-bottom: 1px solid #ddd;
}
-.markdown hr::before {
+.markdown-body hr::before {
display: table;
content: "";
}
-.markdown hr::after {
+.markdown-body hr::after {
display: table;
clear: both;
content: "";
}
-.markdown table {
+.markdown-body table {
border-spacing: 0;
border-collapse: collapse;
}
-.markdown td,
-.markdown th {
+.markdown-body td,
+.markdown-body th {
padding: 0;
}
-.markdown h1,
-.markdown h2,
-.markdown h3,
-.markdown h4,
-.markdown h5,
-.markdown h6 {
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3,
+.markdown-body h4,
+.markdown-body h5,
+.markdown-body h6 {
margin-top: 0;
margin-bottom: 0;
}
-.markdown h1 {
+.markdown-body h1 {
font-size: 32px;
font-weight: 600;
}
-.markdown h2 {
+.markdown-body h2 {
font-size: 24px;
font-weight: 600;
}
-.markdown h3 {
+.markdown-body h3 {
font-size: 20px;
font-weight: 600;
}
-.markdown h4 {
+.markdown-body h4 {
font-size: 16px;
font-weight: 600;
}
-.markdown h5 {
+.markdown-body h5 {
font-size: 14px;
font-weight: 600;
}
-.markdown h6 {
+.markdown-body h6 {
font-size: 12px;
font-weight: 600;
}
-.markdown p {
+.markdown-body p {
margin-top: 0;
margin-bottom: 10px;
}
-.markdown blockquote {
+.markdown-body blockquote {
margin: 0;
}
-.markdown ul,
-.markdown ol {
+.markdown-body ul,
+.markdown-body ol {
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
}
-.markdown ol ol,
-.markdown ul ol {
+.markdown-body ol ol,
+.markdown-body ul ol {
list-style-type: lower-roman;
}
-.markdown ul ul ol,
-.markdown ul ol ol,
-.markdown ol ul ol,
-.markdown ol ol ol {
+.markdown-body ul ul ol,
+.markdown-body ul ol ol,
+.markdown-body ol ul ol,
+.markdown-body ol ol ol {
list-style-type: lower-alpha;
}
-.markdown dd {
+.markdown-body dd {
margin-left: 0;
}
-.markdown code {
+.markdown-body code {
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
}
-.markdown pre {
+.markdown-body pre {
margin-top: 0;
margin-bottom: 0;
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
-.markdown .octicon {
+.markdown-body .octicon {
vertical-align: text-bottom;
}
-.markdown input {
+.markdown-body input {
-webkit-font-feature-settings: "liga" 0;
font-feature-settings: "liga" 0;
}
-.markdown .form-select::-ms-expand {
+.markdown-body .form-select::-ms-expand {
opacity: 0;
}
-.markdown::before {
+.markdown-body::before {
display: table;
content: "";
}
-.markdown::after {
+.markdown-body::after {
display: table;
clear: both;
content: "";
}
-.markdown>*:first-child {
+.markdown-body>*:first-child {
margin-top: 0 !important;
}
-.markdown>*:last-child {
+.markdown-body>*:last-child {
margin-bottom: 0 !important;
}
-.markdown a:not([href]) {
+.markdown-body a:not([href]) {
color: inherit;
text-decoration: none;
}
-.markdown .anchor {
+.markdown-body .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
-.markdown .anchor:focus {
+.markdown-body .anchor:focus {
outline: none;
}
-.markdown p,
-.markdown blockquote,
-.markdown ul,
-.markdown ol,
-.markdown dl,
-.markdown table,
-.markdown pre {
+.markdown-body p,
+.markdown-body blockquote,
+.markdown-body ul,
+.markdown-body ol,
+.markdown-body dl,
+.markdown-body table,
+.markdown-body pre {
margin-top: 0;
margin-bottom: 16px;
}
-.markdown hr {
+.markdown-body hr {
height: 0.25em;
padding: 0;
margin: 24px 0;
@@ -498,21 +498,21 @@
border: 0;
}
-.markdown blockquote {
+.markdown-body blockquote {
padding: 0 1em;
color: #777;
border-left: 0.25em solid #ddd;
}
-.markdown blockquote>:first-child {
+.markdown-body blockquote>:first-child {
margin-top: 0;
}
-.markdown blockquote>:last-child {
+.markdown-body blockquote>:last-child {
margin-bottom: 0;
}
-.markdown kbd {
+.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
@@ -526,102 +526,102 @@
box-shadow: inset 0 -1px 0 #bbb;
}
-.markdown h1,
-.markdown h2,
-.markdown h3,
-.markdown h4,
-.markdown h5,
-.markdown h6 {
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3,
+.markdown-body h4,
+.markdown-body h5,
+.markdown-body h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
-.markdown h1 .octicon-link,
-.markdown h2 .octicon-link,
-.markdown h3 .octicon-link,
-.markdown h4 .octicon-link,
-.markdown h5 .octicon-link,
-.markdown h6 .octicon-link {
+.markdown-body h1 .octicon-link,
+.markdown-body h2 .octicon-link,
+.markdown-body h3 .octicon-link,
+.markdown-body h4 .octicon-link,
+.markdown-body h5 .octicon-link,
+.markdown-body h6 .octicon-link {
color: #000;
vertical-align: middle;
visibility: hidden;
}
-.markdown h1:hover .anchor,
-.markdown h2:hover .anchor,
-.markdown h3:hover .anchor,
-.markdown h4:hover .anchor,
-.markdown h5:hover .anchor,
-.markdown h6:hover .anchor {
+.markdown-body h1:hover .anchor,
+.markdown-body h2:hover .anchor,
+.markdown-body h3:hover .anchor,
+.markdown-body h4:hover .anchor,
+.markdown-body h5:hover .anchor,
+.markdown-body h6:hover .anchor {
text-decoration: none;
}
-.markdown h1:hover .anchor .octicon-link,
-.markdown h2:hover .anchor .octicon-link,
-.markdown h3:hover .anchor .octicon-link,
-.markdown h4:hover .anchor .octicon-link,
-.markdown h5:hover .anchor .octicon-link,
-.markdown h6:hover .anchor .octicon-link {
+.markdown-body h1:hover .anchor .octicon-link,
+.markdown-body h2:hover .anchor .octicon-link,
+.markdown-body h3:hover .anchor .octicon-link,
+.markdown-body h4:hover .anchor .octicon-link,
+.markdown-body h5:hover .anchor .octicon-link,
+.markdown-body h6:hover .anchor .octicon-link {
visibility: visible;
}
-.markdown h1 {
+.markdown-body h1 {
padding-bottom: 0.3em;
font-size: 2em;
border-bottom: 1px solid #eee;
}
-.markdown h2 {
+.markdown-body h2 {
padding-bottom: 0.3em;
font-size: 1.5em;
border-bottom: 1px solid #eee;
}
-.markdown h3 {
+.markdown-body h3 {
font-size: 1.25em;
}
-.markdown h4 {
+.markdown-body h4 {
font-size: 1em;
}
-.markdown h5 {
+.markdown-body h5 {
font-size: 0.875em;
}
-.markdown h6 {
+.markdown-body h6 {
font-size: 0.85em;
color: #777;
}
-.markdown ul,
-.markdown ol {
+.markdown-body ul,
+.markdown-body ol {
padding-left: 2em;
}
-.markdown ul ul,
-.markdown ul ol,
-.markdown ol ol,
-.markdown ol ul {
+.markdown-body ul ul,
+.markdown-body ul ol,
+.markdown-body ol ol,
+.markdown-body ol ul {
margin-top: 0;
margin-bottom: 0;
}
-.markdown li>p {
+.markdown-body li>p {
margin-top: 16px;
}
-.markdown li+li {
+.markdown-body li+li {
margin-top: 0.25em;
}
-.markdown dl {
+.markdown-body dl {
padding: 0;
}
-.markdown dl dt {
+.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
@@ -629,12 +629,12 @@
font-weight: bold;
}
-.markdown dl dd {
+.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
-.markdown table {
+.markdown-body table {
display: block;
width: 100%;
overflow: auto;
@@ -642,32 +642,32 @@
word-break: keep-all;
}
-.markdown table th {
+.markdown-body table th {
font-weight: bold;
}
-.markdown table th,
-.markdown table td {
+.markdown-body table th,
+.markdown-body table td {
padding: 6px 13px;
border: 1px solid #ddd;
}
-.markdown table tr {
+.markdown-body table tr {
background-color: #fff;
border-top: 1px solid #ccc;
}
-.markdown table tr:nth-child(2n) {
+.markdown-body table tr:nth-child(2n) {
background-color: #f8f8f8;
}
-.markdown img {
+.markdown-body img {
max-width: 100%;
box-sizing: content-box;
background-color: #fff;
}
-.markdown code {
+.markdown-body code {
padding: 0;
padding-top: 0.2em;
padding-bottom: 0.2em;
@@ -677,17 +677,17 @@
border-radius: 3px;
}
-.markdown code::before,
-.markdown code::after {
+.markdown-body code::before,
+.markdown-body code::after {
letter-spacing: -0.2em;
content: "\00a0";
}
-.markdown pre {
+.markdown-body pre {
word-wrap: normal;
}
-.markdown pre>code {
+.markdown-body pre>code {
padding: 0;
margin: 0;
font-size: 100%;
@@ -697,17 +697,17 @@
border: 0;
}
-.markdown .highlight {
+.markdown-body .highlight {
margin-bottom: 16px;
}
-.markdown .highlight pre {
+.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal;
}
-.markdown .highlight pre,
-.markdown pre {
+.markdown-body .highlight pre,
+.markdown-body pre {
padding: 16px;
overflow: auto;
font-size: 85%;
@@ -716,7 +716,7 @@
border-radius: 3px;
}
-.markdown pre code {
+.markdown-body pre code {
display: inline;
max-width: auto;
padding: 0;
@@ -728,45 +728,45 @@
border: 0;
}
-.markdown pre code::before,
-.markdown pre code::after {
+.markdown-body pre code::before,
+.markdown-body pre code::after {
content: normal;
}
-.markdown .pl-0 {
+.markdown-body .pl-0 {
padding-left: 0 !important;
}
-.markdown .pl-1 {
+.markdown-body .pl-1 {
padding-left: 3px !important;
}
-.markdown .pl-2 {
+.markdown-body .pl-2 {
padding-left: 6px !important;
}
-.markdown .pl-3 {
+.markdown-body .pl-3 {
padding-left: 12px !important;
}
-.markdown .pl-4 {
+.markdown-body .pl-4 {
padding-left: 24px !important;
}
-.markdown .pl-5 {
+.markdown-body .pl-5 {
padding-left: 36px !important;
}
-.markdown .pl-6 {
+.markdown-body .pl-6 {
padding-left: 48px !important;
}
-.markdown .full-commit .btn-outline:not(:disabled):hover {
+.markdown-body .full-commit .btn-outline:not(:disabled):hover {
color: #4078c0;
border: 1px solid #4078c0;
}
-.markdown kbd {
+.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
@@ -780,25 +780,25 @@
box-shadow: inset 0 -1px 0 #bbb;
}
-.markdown :checked+.radio-label {
+.markdown-body :checked+.radio-label {
position: relative;
z-index: 1;
border-color: #4078c0;
}
-.markdown .task-list-item {
+.markdown-body .task-list-item {
list-style-type: none;
}
-.markdown .task-list-item+.task-list-item {
+.markdown-body .task-list-item+.task-list-item {
margin-top: 3px;
}
-.markdown .task-list-item input {
+.markdown-body .task-list-item input {
margin: 0 0.2em 0.25em -1.6em;
vertical-align: middle;
}
-.markdown hr {
+.markdown-body hr {
border-bottom-color: #eee;
}
From c33c71590ae8dd5fbe09091d95d6f81614bf3e43 Mon Sep 17 00:00:00 2001
From: 1ambda <1amb4a@gmail.com>
Date: Fri, 2 Sep 2016 00:28:24 +0900
Subject: [PATCH 07/18] fix: Remove the ANCHORLINKS option
- added some markdown syntax tests
---
.../apache/zeppelin/markdown/Markdown.java | 20 +-
.../zeppelin/markdown/MarkdownTest.java | 227 ++++++++++++++----
2 files changed, 193 insertions(+), 54 deletions(-)
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java b/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java
index 3394f75b1a9..1ec17d64437 100644
--- a/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java
@@ -44,9 +44,19 @@ public Markdown(Properties property) {
super(property);
}
+ /** wrap with markdown class div to styling DOM using css */
+ public static String wrapHtmlWithMarkdownClassDiv(String html) {
+ return new StringBuilder()
+ .append("
\n")
+ .append(html)
+ .append("\n
")
+ .toString();
+ }
+
@Override
public void open() {
- md = new PegDownProcessor(Extensions.ALL_WITH_OPTIONALS, 5000);
+ int pegdownOptions = Extensions.ALL_WITH_OPTIONALS - Extensions.ANCHORLINKS;
+ md = new PegDownProcessor(pegdownOptions, 5000);
}
@Override
@@ -60,13 +70,7 @@ public InterpreterResult interpret(String st, InterpreterContext interpreterCont
String parsed = md.markdownToHtml(st);
if (null == parsed) throw new RuntimeException("Cannot parse markdown syntax string to HTML");
- /** wrap with markdown class div to support markdown syntax using css */
- html =
- new StringBuilder()
- .append("
\n")
- .append(parsed)
- .append("\n
")
- .toString();
+ html = wrapHtmlWithMarkdownClassDiv(parsed);
} catch (java.lang.RuntimeException e) {
LOGGER.error("Exception in Markdown while interpret ", e);
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java
index f1aa54be427..4d7052661d5 100644
--- a/markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java
+++ b/markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java
@@ -22,7 +22,7 @@
import java.util.Properties;
import org.apache.zeppelin.interpreter.InterpreterResult;
-import org.apache.zeppelin.markdown.Markdown;
+import static org.apache.zeppelin.markdown.Markdown.wrapHtmlWithMarkdownClassDiv;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -42,22 +42,155 @@ public void tearDown() throws Exception {
md.close();
}
+ @Test
+ public void testHeader() {
+ InterpreterResult r1 = md.interpret("# H1", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
H1
"), r1.message());
+
+ InterpreterResult r2 = md.interpret("## H2", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
H2
"), r2.message());
+
+ InterpreterResult r3 = md.interpret("### H3", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
H3
"), r3.message());
+
+ InterpreterResult r4 = md.interpret("#### H4", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
H4
"), r4.message());
+
+ InterpreterResult r5 = md.interpret("##### H5", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
H5
"), r5.message());
+
+ InterpreterResult r6 = md.interpret("###### H6", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
H6
"), r6.message());
+
+ InterpreterResult r7 = md.interpret("Alt-H1\n" + "======", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
Alt-H1
"), r7.message());
+
+ InterpreterResult r8 = md.interpret("Alt-H2\n" + "------", null);
+ assertEquals(wrapHtmlWithMarkdownClassDiv("
Alt-H2
"), r8.message());
+ }
+
@Test
public void testStrikethrough() {
InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
- assertEquals("
This is deleted text
", result.message());
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv("
This is deleted text
"), result.message());
}
@Test
public void testItalics() {
InterpreterResult result = md.interpret("This is *italics* text", null);
- assertEquals("
This is italics text
", result.message());
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv("
This is italics text
"), result.message());
}
@Test
public void testStrongEmphasis() {
InterpreterResult result = md.interpret("This is **strong emphasis** text", null);
- assertEquals("
This is strong emphasis text
", result.message());
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv("
This is strong emphasis text
"),
+ result.message());
+ }
+
+ @Test
+ public void testOrderedList() {
+ InterpreterResult result =
+ md.interpret("1. First ordered list item\n" + "2. Another item", null);
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv(
+ "
\n"
+ + " - First ordered list item
\n"
+ + " - Another item
\n"
+ + "
"),
+ result.message());
+ }
+
+ @Test
+ public void testUnorderedList() {
+ InterpreterResult result =
+ md.interpret(
+ "* Unordered list can use asterisks\n" + "- Or minuses\n" + "+ Or pluses", null);
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv(
+ "
\n"
+ + " - Unordered list can use asterisks
\n"
+ + " - Or minuses
\n"
+ + " - Or pluses
\n"
+ + "
"),
+ result.message());
+ }
+
+ @Test
+ public void testLinks() {
+ InterpreterResult result =
+ md.interpret(
+ "[I'm an inline-style link](https://www.google.com)\n"
+ + "\n"
+ + "[I'm an inline-style link with title](https://www.google.com \"Google's Homepage\")\n"
+ + "\n"
+ + "[I'm a reference-style link][Arbitrary case-insensitive reference text]\n"
+ + "\n"
+ + "[I'm a relative reference to a repository file](../blob/master/LICENSE)\n"
+ + "\n"
+ + "[You can use numbers for reference-style link definitions][1]\n"
+ + "\n"
+ + "Or leave it empty and use the [link text itself].\n"
+ + "\n"
+ + "URLs and URLs in angle brackets will automatically get turned into links. \n"
+ + "http://www.example.com or
and sometimes \n"
+ + "example.com (but not on Github, for example).\n"
+ + "\n"
+ + "Some text to show that the reference links can follow later.\n"
+ + "\n"
+ + "[arbitrary case-insensitive reference text]: https://www.mozilla.org\n"
+ + "[1]: http://slashdot.org\n"
+ + "[link text itself]: http://www.reddit.com",
+ null);
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv(
+ "I’m an inline-style link
\n"
+ + "I’m an inline-style link with title
\n"
+ + "I’m a reference-style link
\n"
+ + "I’m a relative reference to a repository file
\n"
+ + "You can use numbers for reference-style link definitions
\n"
+ + "Or leave it empty and use the link text itself.
\n"
+ + "URLs and URLs in angle brackets will automatically get turned into links.
http://www.example.com or http://www.example.com and sometimes
example.com (but not on Github, for example).
\n"
+ + "Some text to show that the reference links can follow later.
"),
+ result.message());
+ }
+
+ @Test
+ public void testInlineCode() {
+ InterpreterResult result = md.interpret("Inline `code` has `back-ticks around` it.", null);
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv(
+ "Inline code has back-ticks around it.
"),
+ result.message());
+ }
+
+ @Test
+ public void testBlockQuotes() {
+ InterpreterResult r1 =
+ md.interpret(
+ "> Blockquotes are very handy in email to emulate reply text.\n"
+ + "> This line is part of the same quote.",
+ null);
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv(
+ "\n"
+ + " Blockquotes are very handy in email to emulate reply text.
This line is part of the same quote.
\n"
+ + "
"),
+ r1.message());
+
+ InterpreterResult r2 =
+ md.interpret(
+ "> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. ",
+ null);
+ assertEquals(
+ wrapHtmlWithMarkdownClassDiv(
+ "\n"
+ + " This is a very long line that will still be quoted properly when it wraps. Oh boy let’s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can put Markdown into a blockquote.
\n"
+ + "
"),
+ r2.message());
}
@Test
@@ -71,27 +204,28 @@ public void testSimpleTable() {
null);
assertEquals(
- "\n"
- + " \n"
- + " \n"
- + " | Markdown | \n"
- + " Less | \n"
- + " Pretty | \n"
- + "
\n"
- + " \n"
- + " \n"
- + " \n"
- + " | Still | \n"
- + " renders | \n"
- + " nicely | \n"
- + "
\n"
- + " \n"
- + " | 1 | \n"
- + " 2 | \n"
- + " 3 | \n"
- + "
\n"
- + " \n"
- + "
",
+ wrapHtmlWithMarkdownClassDiv(
+ "\n"
+ + " \n"
+ + " \n"
+ + " | Markdown | \n"
+ + " Less | \n"
+ + " Pretty | \n"
+ + "
\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " | Still | \n"
+ + " renders | \n"
+ + " nicely | \n"
+ + "
\n"
+ + " \n"
+ + " | 1 | \n"
+ + " 2 | \n"
+ + " 3 | \n"
+ + "
\n"
+ + " \n"
+ + "
"),
result.message());
}
@@ -106,27 +240,28 @@ public void testAlignedTable() {
null);
assertEquals(
- "\n"
- + " \n"
- + " \n"
- + " | First Header | \n"
- + " Second Header | \n"
- + " Third Header | \n"
- + "
\n"
- + " \n"
- + " \n"
- + " \n"
- + " | First row | \n"
- + " Data | \n"
- + " Very long data entry | \n"
- + "
\n"
- + " \n"
- + " | Second row | \n"
- + " Cell | \n"
- + " Cell | \n"
- + "
\n"
- + " \n"
- + "
",
+ wrapHtmlWithMarkdownClassDiv(
+ "\n"
+ + " \n"
+ + " \n"
+ + " | First Header | \n"
+ + " Second Header | \n"
+ + " Third Header | \n"
+ + "
\n"
+ + " \n"
+ + " \n"
+ + " \n"
+ + " | First row | \n"
+ + " Data | \n"
+ + " Very long data entry | \n"
+ + "
\n"
+ + " \n"
+ + " | Second row | \n"
+ + " Cell | \n"
+ + " Cell | \n"
+ + "
\n"
+ + " \n"
+ + "
"),
result.message());
}
}
From 55a2f1025483b8be705bfd94734c968a6acfa9ce Mon Sep 17 00:00:00 2001
From: 1ambda <1amb4a@gmail.com>
Date: Fri, 2 Sep 2016 23:24:35 +0900
Subject: [PATCH 08/18] fix: Add MarkdownParser interface to support mulitple
parsers
---
...Markdown.java => MarkdownInterpreter.java} | 41 +--
.../zeppelin/markdown/MarkdownParser.java | 6 +
.../zeppelin/markdown/PegdownParser.java | 45 +++
.../zeppelin/markdown/MarkdownTest.java | 267 ----------------
.../zeppelin/markdown/PegdownParserTest.java | 302 ++++++++++++++++++
5 files changed, 365 insertions(+), 296 deletions(-)
rename markdown/src/main/java/org/apache/zeppelin/markdown/{Markdown.java => MarkdownInterpreter.java} (62%)
create mode 100644 markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownParser.java
create mode 100644 markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
delete mode 100644 markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java
create mode 100644 markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
similarity index 62%
rename from markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java
rename to markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
index 1ec17d64437..c216baa4cab 100644
--- a/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
@@ -28,52 +28,35 @@
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
import org.apache.zeppelin.scheduler.Scheduler;
import org.apache.zeppelin.scheduler.SchedulerFactory;
-import org.pegdown.Extensions;
-import org.pegdown.LinkRenderer;
-import org.pegdown.PegDownProcessor;
-import org.pegdown.ast.RootNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Markdown interpreter for Zeppelin. */
-public class Markdown extends Interpreter {
- private PegDownProcessor md;
- static final Logger LOGGER = LoggerFactory.getLogger(Markdown.class);
+/** MarkdownInterpreter interpreter for Zeppelin. */
+public class MarkdownInterpreter extends Interpreter {
+ private final Logger logger = LoggerFactory.getLogger(MarkdownInterpreter.class);
- public Markdown(Properties property) {
- super(property);
- }
+ private MarkdownParser parser;
- /** wrap with markdown class div to styling DOM using css */
- public static String wrapHtmlWithMarkdownClassDiv(String html) {
- return new StringBuilder()
- .append("\n")
- .append(html)
- .append("\n
")
- .toString();
+ public MarkdownInterpreter(Properties property) {
+ super(property);
}
@Override
public void open() {
- int pegdownOptions = Extensions.ALL_WITH_OPTIONALS - Extensions.ANCHORLINKS;
- md = new PegDownProcessor(pegdownOptions, 5000);
+ parser = new PegdownParser();
}
@Override
public void close() {}
@Override
- public InterpreterResult interpret(String st, InterpreterContext interpreterContext) {
+ public InterpreterResult interpret(String markdownText, InterpreterContext interpreterContext) {
String html;
try {
- String parsed = md.markdownToHtml(st);
- if (null == parsed) throw new RuntimeException("Cannot parse markdown syntax string to HTML");
-
- html = wrapHtmlWithMarkdownClassDiv(parsed);
-
- } catch (java.lang.RuntimeException e) {
- LOGGER.error("Exception in Markdown while interpret ", e);
+ html = parser.render(markdownText);
+ } catch (Exception e) {
+ logger.error("Exception in MarkdownInterpreter while interpret ", e);
return new InterpreterResult(Code.ERROR, InterpreterUtils.getMostRelevantMessage(e));
}
@@ -96,7 +79,7 @@ public int getProgress(InterpreterContext context) {
@Override
public Scheduler getScheduler() {
return SchedulerFactory.singleton()
- .createOrGetParallelScheduler(Markdown.class.getName() + this.hashCode(), 5);
+ .createOrGetParallelScheduler(MarkdownInterpreter.class.getName() + this.hashCode(), 5);
}
@Override
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownParser.java b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownParser.java
new file mode 100644
index 00000000000..7266e004a57
--- /dev/null
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownParser.java
@@ -0,0 +1,6 @@
+package org.apache.zeppelin.markdown;
+
+/** Abstract Markdown Parser. */
+public interface MarkdownParser {
+ String render(String markdownText);
+}
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java b/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
new file mode 100644
index 00000000000..7d71721ccb9
--- /dev/null
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
@@ -0,0 +1,45 @@
+package org.apache.zeppelin.markdown;
+
+import org.pegdown.Extensions;
+import org.pegdown.PegDownProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Pegdown Markdown Parser. */
+public class PegdownParser implements MarkdownParser {
+ private PegDownProcessor processor;
+
+ private final Logger logger = LoggerFactory.getLogger(PegdownParser.class);
+
+ public PegdownParser() {
+ int pegdownOptions = Extensions.ALL_WITH_OPTIONALS - Extensions.ANCHORLINKS;
+ int parsingTimeoutAsMillis = 5000;
+ processor = new PegDownProcessor(pegdownOptions, parsingTimeoutAsMillis);
+ }
+
+ @Override
+ public String render(String markdownText) {
+ String html = "";
+
+ try {
+ String parsed = processor.markdownToHtml(markdownText);
+ if (null == parsed) throw new RuntimeException("Cannot parse markdown syntax string to HTML");
+
+ html = wrapWithMarkdownClassDiv(parsed);
+
+ } catch (RuntimeException e) {
+ logger.error("Failed to parsed markdown text", e);
+ }
+
+ return html;
+ }
+
+ /** wrap with markdown class div to styling DOM using css */
+ public static String wrapWithMarkdownClassDiv(String html) {
+ return new StringBuilder()
+ .append("\n")
+ .append(html)
+ .append("\n
")
+ .toString();
+ }
+}
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java
deleted file mode 100644
index 4d7052661d5..00000000000
--- a/markdown/src/test/java/org/apache/zeppelin/markdown/MarkdownTest.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.zeppelin.markdown;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Properties;
-
-import org.apache.zeppelin.interpreter.InterpreterResult;
-import static org.apache.zeppelin.markdown.Markdown.wrapHtmlWithMarkdownClassDiv;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class MarkdownTest {
-
- Markdown md;
-
- @Before
- public void setUp() throws Exception {
- md = new Markdown(new Properties());
- md.open();
- }
-
- @After
- public void tearDown() throws Exception {
- md.close();
- }
-
- @Test
- public void testHeader() {
- InterpreterResult r1 = md.interpret("# H1", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("H1
"), r1.message());
-
- InterpreterResult r2 = md.interpret("## H2", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("H2
"), r2.message());
-
- InterpreterResult r3 = md.interpret("### H3", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("H3
"), r3.message());
-
- InterpreterResult r4 = md.interpret("#### H4", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("H4
"), r4.message());
-
- InterpreterResult r5 = md.interpret("##### H5", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("H5
"), r5.message());
-
- InterpreterResult r6 = md.interpret("###### H6", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("H6
"), r6.message());
-
- InterpreterResult r7 = md.interpret("Alt-H1\n" + "======", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("Alt-H1
"), r7.message());
-
- InterpreterResult r8 = md.interpret("Alt-H2\n" + "------", null);
- assertEquals(wrapHtmlWithMarkdownClassDiv("Alt-H2
"), r8.message());
- }
-
- @Test
- public void testStrikethrough() {
- InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv("This is deleted text
"), result.message());
- }
-
- @Test
- public void testItalics() {
- InterpreterResult result = md.interpret("This is *italics* text", null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv("This is italics text
"), result.message());
- }
-
- @Test
- public void testStrongEmphasis() {
- InterpreterResult result = md.interpret("This is **strong emphasis** text", null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv("This is strong emphasis text
"),
- result.message());
- }
-
- @Test
- public void testOrderedList() {
- InterpreterResult result =
- md.interpret("1. First ordered list item\n" + "2. Another item", null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "\n"
- + " - First ordered list item
\n"
- + " - Another item
\n"
- + "
"),
- result.message());
- }
-
- @Test
- public void testUnorderedList() {
- InterpreterResult result =
- md.interpret(
- "* Unordered list can use asterisks\n" + "- Or minuses\n" + "+ Or pluses", null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "\n"
- + " - Unordered list can use asterisks
\n"
- + " - Or minuses
\n"
- + " - Or pluses
\n"
- + "
"),
- result.message());
- }
-
- @Test
- public void testLinks() {
- InterpreterResult result =
- md.interpret(
- "[I'm an inline-style link](https://www.google.com)\n"
- + "\n"
- + "[I'm an inline-style link with title](https://www.google.com \"Google's Homepage\")\n"
- + "\n"
- + "[I'm a reference-style link][Arbitrary case-insensitive reference text]\n"
- + "\n"
- + "[I'm a relative reference to a repository file](../blob/master/LICENSE)\n"
- + "\n"
- + "[You can use numbers for reference-style link definitions][1]\n"
- + "\n"
- + "Or leave it empty and use the [link text itself].\n"
- + "\n"
- + "URLs and URLs in angle brackets will automatically get turned into links. \n"
- + "http://www.example.com or and sometimes \n"
- + "example.com (but not on Github, for example).\n"
- + "\n"
- + "Some text to show that the reference links can follow later.\n"
- + "\n"
- + "[arbitrary case-insensitive reference text]: https://www.mozilla.org\n"
- + "[1]: http://slashdot.org\n"
- + "[link text itself]: http://www.reddit.com",
- null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "I’m an inline-style link
\n"
- + "I’m an inline-style link with title
\n"
- + "I’m a reference-style link
\n"
- + "I’m a relative reference to a repository file
\n"
- + "You can use numbers for reference-style link definitions
\n"
- + "Or leave it empty and use the link text itself.
\n"
- + "URLs and URLs in angle brackets will automatically get turned into links.
http://www.example.com or http://www.example.com and sometimes
example.com (but not on Github, for example).
\n"
- + "Some text to show that the reference links can follow later.
"),
- result.message());
- }
-
- @Test
- public void testInlineCode() {
- InterpreterResult result = md.interpret("Inline `code` has `back-ticks around` it.", null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "Inline code has back-ticks around it.
"),
- result.message());
- }
-
- @Test
- public void testBlockQuotes() {
- InterpreterResult r1 =
- md.interpret(
- "> Blockquotes are very handy in email to emulate reply text.\n"
- + "> This line is part of the same quote.",
- null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "\n"
- + " Blockquotes are very handy in email to emulate reply text.
This line is part of the same quote.
\n"
- + "
"),
- r1.message());
-
- InterpreterResult r2 =
- md.interpret(
- "> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. ",
- null);
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "\n"
- + " This is a very long line that will still be quoted properly when it wraps. Oh boy let’s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can put Markdown into a blockquote.
\n"
- + "
"),
- r2.message());
- }
-
- @Test
- public void testSimpleTable() {
- InterpreterResult result =
- md.interpret(
- "Markdown | Less | Pretty\n"
- + "--- | --- | ---\n"
- + "*Still* | `renders` | **nicely**\n"
- + "1 | 2 | 3",
- null);
-
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "\n"
- + " \n"
- + " \n"
- + " | Markdown | \n"
- + " Less | \n"
- + " Pretty | \n"
- + "
\n"
- + " \n"
- + " \n"
- + " \n"
- + " | Still | \n"
- + " renders | \n"
- + " nicely | \n"
- + "
\n"
- + " \n"
- + " | 1 | \n"
- + " 2 | \n"
- + " 3 | \n"
- + "
\n"
- + " \n"
- + "
"),
- result.message());
- }
-
- @Test
- public void testAlignedTable() {
- InterpreterResult result =
- md.interpret(
- "| First Header | Second Header | Third Header |\n"
- + "| :----------- | :-----------: | -------------------: |\n"
- + "| First row | Data | Very long data entry |\n"
- + "| Second row | **Cell** | *Cell* |",
- null);
-
- assertEquals(
- wrapHtmlWithMarkdownClassDiv(
- "\n"
- + " \n"
- + " \n"
- + " | First Header | \n"
- + " Second Header | \n"
- + " Third Header | \n"
- + "
\n"
- + " \n"
- + " \n"
- + " \n"
- + " | First row | \n"
- + " Data | \n"
- + " Very long data entry | \n"
- + "
\n"
- + " \n"
- + " | Second row | \n"
- + " Cell | \n"
- + " Cell | \n"
- + "
\n"
- + " \n"
- + "
"),
- result.message());
- }
-}
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
new file mode 100644
index 00000000000..aed71ccbae5
--- /dev/null
+++ b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zeppelin.markdown;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Properties;
+
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import static org.apache.zeppelin.markdown.PegdownParser.wrapWithMarkdownClassDiv;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PegdownParserTest {
+
+ MarkdownInterpreter pegdownInterpreter;
+
+ @Before
+ public void setUp() throws Exception {
+ pegdownInterpreter = new MarkdownInterpreter(new Properties());
+ pegdownInterpreter.open();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ pegdownInterpreter.close();
+ }
+
+ @Test
+ public void testHeader() {
+ InterpreterResult r1 = pegdownInterpreter.interpret("# H1", null);
+ assertEquals(wrapWithMarkdownClassDiv("H1
"), r1.message());
+
+ InterpreterResult r2 = pegdownInterpreter.interpret("## H2", null);
+ assertEquals(wrapWithMarkdownClassDiv("H2
"), r2.message());
+
+ InterpreterResult r3 = pegdownInterpreter.interpret("### H3", null);
+ assertEquals(wrapWithMarkdownClassDiv("H3
"), r3.message());
+
+ InterpreterResult r4 = pegdownInterpreter.interpret("#### H4", null);
+ assertEquals(wrapWithMarkdownClassDiv("H4
"), r4.message());
+
+ InterpreterResult r5 = pegdownInterpreter.interpret("##### H5", null);
+ assertEquals(wrapWithMarkdownClassDiv("H5
"), r5.message());
+
+ InterpreterResult r6 = pegdownInterpreter.interpret("###### H6", null);
+ assertEquals(wrapWithMarkdownClassDiv("H6
"), r6.message());
+
+ InterpreterResult r7 = pegdownInterpreter.interpret("Alt-H1\n" + "======", null);
+ assertEquals(wrapWithMarkdownClassDiv("Alt-H1
"), r7.message());
+
+ InterpreterResult r8 = pegdownInterpreter.interpret("Alt-H2\n" + "------", null);
+ assertEquals(wrapWithMarkdownClassDiv("Alt-H2
"), r8.message());
+ }
+
+ @Test
+ public void testStrikethrough() {
+ InterpreterResult result = pegdownInterpreter.interpret("This is ~~deleted~~ text", null);
+ assertEquals(
+ wrapWithMarkdownClassDiv("This is deleted text
"), result.message());
+ }
+
+ @Test
+ public void testItalics() {
+ InterpreterResult result = pegdownInterpreter.interpret("This is *italics* text", null);
+ assertEquals(
+ wrapWithMarkdownClassDiv("This is italics text
"), result.message());
+ }
+
+ @Test
+ public void testStrongEmphasis() {
+ InterpreterResult result =
+ pegdownInterpreter.interpret("This is **strong emphasis** text", null);
+ assertEquals(
+ wrapWithMarkdownClassDiv("This is strong emphasis text
"),
+ result.message());
+ }
+
+ @Test
+ public void testOrderedList() {
+ String input =
+ new StringBuilder()
+ .append("1. First ordered list item\n")
+ .append("2. Another item")
+ .toString();
+
+ String expected =
+ new StringBuilder()
+ .append("\n")
+ .append(" - First ordered list item
\n")
+ .append(" - Another item
\n")
+ .append("
")
+ .toString();
+
+ InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+ }
+
+ @Test
+ public void testUnorderedList() {
+ String input =
+ new StringBuilder()
+ .append("* Unordered list can use asterisks\n")
+ .append("- Or minuses\n")
+ .append("+ Or pluses")
+ .toString();
+
+ String expected =
+ new StringBuilder()
+ .append("\n")
+ .append(" - Unordered list can use asterisks
\n")
+ .append(" - Or minuses
\n")
+ .append(" - Or pluses
\n")
+ .append("
")
+ .toString();
+
+ InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+ }
+
+ @Test
+ public void testLinks() {
+ String input =
+ new StringBuilder()
+ .append("[I'm an inline-style link](https://www.google.com)\n")
+ .append("\n")
+ .append(
+ "[I'm an inline-style link with title](https://www.google.com \"Google's Homepage\")\n")
+ .append("\n")
+ .append("[I'm a reference-style link][Arbitrary case-insensitive reference text]\n")
+ .append("\n")
+ .append("[I'm a relative reference to a repository file](../blob/master/LICENSE)\n")
+ .append("\n")
+ .append("[You can use numbers for reference-style link definitions][1]\n")
+ .append("\n")
+ .append("Or leave it empty and use the [link text itself].\n")
+ .append("\n")
+ .append("URLs and URLs in angle brackets will automatically get turned into links. \n")
+ .append("http://www.example.com or and sometimes \n")
+ .append("example.com (but not on Github, for example).\n")
+ .append("\n")
+ .append("Some text to show that the reference links can follow later.\n")
+ .append("\n")
+ .append("[arbitrary case-insensitive reference text]: https://www.mozilla.org\n")
+ .append("[1]: http://slashdot.org\n")
+ .append("[link text itself]: http://www.reddit.com")
+ .toString();
+
+ String expected =
+ new StringBuilder()
+ .append(
+ "I’m an inline-style link
\n")
+ .append(
+ "I’m an inline-style link with title
\n")
+ .append(
+ "I’m a reference-style link
\n")
+ .append(
+ "I’m a relative reference to a repository file
\n")
+ .append(
+ "You can use numbers for reference-style link definitions
\n")
+ .append(
+ "Or leave it empty and use the link text itself.
\n")
+ .append(
+ "URLs and URLs in angle brackets will automatically get turned into links.
http://www.example.com or http://www.example.com and sometimes
example.com (but not on Github, for example).
\n")
+ .append("Some text to show that the reference links can follow later.
")
+ .toString();
+
+ InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+ }
+
+ @Test
+ public void testInlineCode() {
+ InterpreterResult result =
+ pegdownInterpreter.interpret("Inline `code` has `back-ticks around` it.", null);
+ assertEquals(
+ wrapWithMarkdownClassDiv(
+ "Inline code has back-ticks around it.
"),
+ result.message());
+ }
+
+ @Test
+ public void testBlockQuotes() {
+ InterpreterResult r1 =
+ pegdownInterpreter.interpret(
+ "> Blockquotes are very handy in email to emulate reply text.\n"
+ + "> This line is part of the same quote.",
+ null);
+ assertEquals(
+ wrapWithMarkdownClassDiv(
+ "\n"
+ + " Blockquotes are very handy in email to emulate reply text.
This line is part of the same quote.
\n"
+ + "
"),
+ r1.message());
+
+ InterpreterResult r2 =
+ pegdownInterpreter.interpret(
+ "> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **MarkdownInterpreter** into a blockquote. ",
+ null);
+ assertEquals(
+ wrapWithMarkdownClassDiv(
+ "\n"
+ + " This is a very long line that will still be quoted properly when it wraps. Oh boy let’s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can put MarkdownInterpreter into a blockquote.
\n"
+ + "
"),
+ r2.message());
+ }
+
+ @Test
+ public void testSimpleTable() {
+ String input =
+ new StringBuilder()
+ .append("MarkdownInterpreter | Less | Pretty\n")
+ .append("--- | --- | ---\n")
+ .append("*Still* | `renders` | **nicely**\n")
+ .append("1 | 2 | 3")
+ .toString();
+
+ String expected =
+ new StringBuilder()
+ .append("\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | MarkdownInterpreter | \n")
+ .append(" Less | \n")
+ .append(" Pretty | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | Still | \n")
+ .append(" renders | \n")
+ .append(" nicely | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" | 1 | \n")
+ .append(" 2 | \n")
+ .append(" 3 | \n")
+ .append("
\n")
+ .append(" \n")
+ .append("
")
+ .toString();
+
+ InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+ }
+
+ @Test
+ public void testAlignedTable() {
+
+ String input =
+ new StringBuilder()
+ .append("| First Header | Second Header | Third Header |\n")
+ .append("| :----------- | :-----------: | -------------------: |\n")
+ .append("| First row | Data | Very long data entry |\n")
+ .append("| Second row | **Cell** | *Cell* |")
+ .toString();
+
+ String expected =
+ new StringBuilder()
+ .append("\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | First Header | \n")
+ .append(" Second Header | \n")
+ .append(" Third Header | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | First row | \n")
+ .append(" Data | \n")
+ .append(" Very long data entry | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" | Second row | \n")
+ .append(" Cell | \n")
+ .append(" Cell | \n")
+ .append("
\n")
+ .append(" \n")
+ .append("
")
+ .toString();
+
+ InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+ }
+}
From bf9100de31fb630ea19c61eda0f9b89f8382dd20 Mon Sep 17 00:00:00 2001
From: 1ambda <1amb4a@gmail.com>
Date: Fri, 2 Sep 2016 23:25:03 +0900
Subject: [PATCH 09/18] chore: Restore markdown4j dependency
---
markdown/pom.xml | 6 ++++++
zeppelin-distribution/src/bin_license/LICENSE | 1 +
2 files changed, 7 insertions(+)
diff --git a/markdown/pom.xml b/markdown/pom.xml
index 9d089474ad5..6f25f3e8e1e 100644
--- a/markdown/pom.xml
+++ b/markdown/pom.xml
@@ -45,6 +45,12 @@
slf4j-api
+
+ org.commonjava.googlecode.markdown4j
+ markdown4j
+ 2.2-cj-1.0
+
+
org.pegdown
pegdown
diff --git a/zeppelin-distribution/src/bin_license/LICENSE b/zeppelin-distribution/src/bin_license/LICENSE
index 83905f5f4e0..3ac76f303f8 100644
--- a/zeppelin-distribution/src/bin_license/LICENSE
+++ b/zeppelin-distribution/src/bin_license/LICENSE
@@ -199,6 +199,7 @@ The following components are provided under the BSD-style License.
(BSD-like) ASM asm-tree (org.ow2.asm:asm-tree:5.0.3 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
(BSD-like) ASM asm-analysis (org.ow2.asm:asm-analysis:5.0.3 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
(BSD-like) ASM asm-utils (org.ow2.asm:asm-utils:5.0.3 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
+ (New BSD License) Markdown4j (org.commonjava.googlecode.markdown4j:markdown4j:jar:2.2-cj-1.0 - https://code.google.com/p/markdown4j/)
(New BSD License) Py4J (net.sf.py4j:py4j:0.9 - http://py4j.sourceforge.net/)
(New BSD License) Py4J (net.sf.py4j:py4j:0.10.1 - http://py4j.sourceforge.net/) - https://github.com/bartdag/py4j/blob/0.10.1/LICENSE.txt
(BSD 3 Clause) Paranamer (com.thoughtworks.paranamer:paranamer:jar:2.6) - https://github.com/paul-hammant/paranamer/blob/paranamer-parent-2.6/LICENSE.txt
From d2d4455118191dfc89fc789dfefecfccc4c210cb Mon Sep 17 00:00:00 2001
From: 1ambda <1amb4a@gmail.com>
Date: Fri, 2 Sep 2016 23:28:50 +0900
Subject: [PATCH 10/18] style: Reformat using intellij-java-google-style
---
.../markdown/MarkdownInterpreter.java | 2 +-
.../zeppelin/markdown/PegdownParser.java | 9 +-
.../zeppelin/markdown/PegdownParserTest.java | 294 +++++++++---------
3 files changed, 152 insertions(+), 153 deletions(-)
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
index c216baa4cab..84bb7c1e766 100644
--- a/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
@@ -79,7 +79,7 @@ public int getProgress(InterpreterContext context) {
@Override
public Scheduler getScheduler() {
return SchedulerFactory.singleton()
- .createOrGetParallelScheduler(MarkdownInterpreter.class.getName() + this.hashCode(), 5);
+ .createOrGetParallelScheduler(MarkdownInterpreter.class.getName() + this.hashCode(), 5);
}
@Override
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java b/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
index 7d71721ccb9..2236785d6b2 100644
--- a/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
@@ -26,7 +26,6 @@ public String render(String markdownText) {
if (null == parsed) throw new RuntimeException("Cannot parse markdown syntax string to HTML");
html = wrapWithMarkdownClassDiv(parsed);
-
} catch (RuntimeException e) {
logger.error("Failed to parsed markdown text", e);
}
@@ -37,9 +36,9 @@ public String render(String markdownText) {
/** wrap with markdown class div to styling DOM using css */
public static String wrapWithMarkdownClassDiv(String html) {
return new StringBuilder()
- .append("\n")
- .append(html)
- .append("\n
")
- .toString();
+ .append("\n")
+ .append(html)
+ .append("\n
")
+ .toString();
}
}
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
index aed71ccbae5..9cea9d3921e 100644
--- a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
+++ b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
@@ -73,40 +73,40 @@ public void testHeader() {
public void testStrikethrough() {
InterpreterResult result = pegdownInterpreter.interpret("This is ~~deleted~~ text", null);
assertEquals(
- wrapWithMarkdownClassDiv("This is deleted text
"), result.message());
+ wrapWithMarkdownClassDiv("This is deleted text
"), result.message());
}
@Test
public void testItalics() {
InterpreterResult result = pegdownInterpreter.interpret("This is *italics* text", null);
assertEquals(
- wrapWithMarkdownClassDiv("This is italics text
"), result.message());
+ wrapWithMarkdownClassDiv("This is italics text
"), result.message());
}
@Test
public void testStrongEmphasis() {
InterpreterResult result =
- pegdownInterpreter.interpret("This is **strong emphasis** text", null);
+ pegdownInterpreter.interpret("This is **strong emphasis** text", null);
assertEquals(
- wrapWithMarkdownClassDiv("This is strong emphasis text
"),
- result.message());
+ wrapWithMarkdownClassDiv("This is strong emphasis text
"),
+ result.message());
}
@Test
public void testOrderedList() {
String input =
- new StringBuilder()
- .append("1. First ordered list item\n")
- .append("2. Another item")
- .toString();
+ new StringBuilder()
+ .append("1. First ordered list item\n")
+ .append("2. Another item")
+ .toString();
String expected =
- new StringBuilder()
- .append("\n")
- .append(" - First ordered list item
\n")
- .append(" - Another item
\n")
- .append("
")
- .toString();
+ new StringBuilder()
+ .append("\n")
+ .append(" - First ordered list item
\n")
+ .append(" - Another item
\n")
+ .append("
")
+ .toString();
InterpreterResult result = pegdownInterpreter.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
@@ -115,20 +115,20 @@ public void testOrderedList() {
@Test
public void testUnorderedList() {
String input =
- new StringBuilder()
- .append("* Unordered list can use asterisks\n")
- .append("- Or minuses\n")
- .append("+ Or pluses")
- .toString();
+ new StringBuilder()
+ .append("* Unordered list can use asterisks\n")
+ .append("- Or minuses\n")
+ .append("+ Or pluses")
+ .toString();
String expected =
- new StringBuilder()
- .append("\n")
- .append(" - Unordered list can use asterisks
\n")
- .append(" - Or minuses
\n")
- .append(" - Or pluses
\n")
- .append("
")
- .toString();
+ new StringBuilder()
+ .append("\n")
+ .append(" - Unordered list can use asterisks
\n")
+ .append(" - Or minuses
\n")
+ .append(" - Or pluses
\n")
+ .append("
")
+ .toString();
InterpreterResult result = pegdownInterpreter.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
@@ -137,49 +137,49 @@ public void testUnorderedList() {
@Test
public void testLinks() {
String input =
- new StringBuilder()
- .append("[I'm an inline-style link](https://www.google.com)\n")
- .append("\n")
- .append(
- "[I'm an inline-style link with title](https://www.google.com \"Google's Homepage\")\n")
- .append("\n")
- .append("[I'm a reference-style link][Arbitrary case-insensitive reference text]\n")
- .append("\n")
- .append("[I'm a relative reference to a repository file](../blob/master/LICENSE)\n")
- .append("\n")
- .append("[You can use numbers for reference-style link definitions][1]\n")
- .append("\n")
- .append("Or leave it empty and use the [link text itself].\n")
- .append("\n")
- .append("URLs and URLs in angle brackets will automatically get turned into links. \n")
- .append("http://www.example.com or and sometimes \n")
- .append("example.com (but not on Github, for example).\n")
- .append("\n")
- .append("Some text to show that the reference links can follow later.\n")
- .append("\n")
- .append("[arbitrary case-insensitive reference text]: https://www.mozilla.org\n")
- .append("[1]: http://slashdot.org\n")
- .append("[link text itself]: http://www.reddit.com")
- .toString();
+ new StringBuilder()
+ .append("[I'm an inline-style link](https://www.google.com)\n")
+ .append("\n")
+ .append(
+ "[I'm an inline-style link with title](https://www.google.com \"Google's Homepage\")\n")
+ .append("\n")
+ .append("[I'm a reference-style link][Arbitrary case-insensitive reference text]\n")
+ .append("\n")
+ .append("[I'm a relative reference to a repository file](../blob/master/LICENSE)\n")
+ .append("\n")
+ .append("[You can use numbers for reference-style link definitions][1]\n")
+ .append("\n")
+ .append("Or leave it empty and use the [link text itself].\n")
+ .append("\n")
+ .append("URLs and URLs in angle brackets will automatically get turned into links. \n")
+ .append("http://www.example.com or and sometimes \n")
+ .append("example.com (but not on Github, for example).\n")
+ .append("\n")
+ .append("Some text to show that the reference links can follow later.\n")
+ .append("\n")
+ .append("[arbitrary case-insensitive reference text]: https://www.mozilla.org\n")
+ .append("[1]: http://slashdot.org\n")
+ .append("[link text itself]: http://www.reddit.com")
+ .toString();
String expected =
- new StringBuilder()
- .append(
- "I’m an inline-style link
\n")
- .append(
- "I’m an inline-style link with title
\n")
- .append(
- "I’m a reference-style link
\n")
- .append(
- "I’m a relative reference to a repository file
\n")
- .append(
- "You can use numbers for reference-style link definitions
\n")
- .append(
- "Or leave it empty and use the link text itself.
\n")
- .append(
- "URLs and URLs in angle brackets will automatically get turned into links.
http://www.example.com or http://www.example.com and sometimes
example.com (but not on Github, for example).
\n")
- .append("Some text to show that the reference links can follow later.
")
- .toString();
+ new StringBuilder()
+ .append(
+ "I’m an inline-style link
\n")
+ .append(
+ "I’m an inline-style link with title
\n")
+ .append(
+ "I’m a reference-style link
\n")
+ .append(
+ "I’m a relative reference to a repository file
\n")
+ .append(
+ "You can use numbers for reference-style link definitions
\n")
+ .append(
+ "Or leave it empty and use the link text itself.
\n")
+ .append(
+ "URLs and URLs in angle brackets will automatically get turned into links.
http://www.example.com or http://www.example.com and sometimes
example.com (but not on Github, for example).
\n")
+ .append("Some text to show that the reference links can follow later.
")
+ .toString();
InterpreterResult result = pegdownInterpreter.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
@@ -188,73 +188,73 @@ public void testLinks() {
@Test
public void testInlineCode() {
InterpreterResult result =
- pegdownInterpreter.interpret("Inline `code` has `back-ticks around` it.", null);
+ pegdownInterpreter.interpret("Inline `code` has `back-ticks around` it.", null);
assertEquals(
- wrapWithMarkdownClassDiv(
- "Inline code has back-ticks around it.
"),
- result.message());
+ wrapWithMarkdownClassDiv(
+ "Inline code has back-ticks around it.
"),
+ result.message());
}
@Test
public void testBlockQuotes() {
InterpreterResult r1 =
- pegdownInterpreter.interpret(
- "> Blockquotes are very handy in email to emulate reply text.\n"
- + "> This line is part of the same quote.",
- null);
+ pegdownInterpreter.interpret(
+ "> Blockquotes are very handy in email to emulate reply text.\n"
+ + "> This line is part of the same quote.",
+ null);
assertEquals(
- wrapWithMarkdownClassDiv(
- "\n"
- + " Blockquotes are very handy in email to emulate reply text.
This line is part of the same quote.
\n"
- + "
"),
- r1.message());
+ wrapWithMarkdownClassDiv(
+ "\n"
+ + " Blockquotes are very handy in email to emulate reply text.
This line is part of the same quote.
\n"
+ + "
"),
+ r1.message());
InterpreterResult r2 =
- pegdownInterpreter.interpret(
- "> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **MarkdownInterpreter** into a blockquote. ",
- null);
+ pegdownInterpreter.interpret(
+ "> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **MarkdownInterpreter** into a blockquote. ",
+ null);
assertEquals(
- wrapWithMarkdownClassDiv(
- "\n"
- + " This is a very long line that will still be quoted properly when it wraps. Oh boy let’s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can put MarkdownInterpreter into a blockquote.
\n"
- + "
"),
- r2.message());
+ wrapWithMarkdownClassDiv(
+ "\n"
+ + " This is a very long line that will still be quoted properly when it wraps. Oh boy let’s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can put MarkdownInterpreter into a blockquote.
\n"
+ + "
"),
+ r2.message());
}
@Test
public void testSimpleTable() {
String input =
- new StringBuilder()
- .append("MarkdownInterpreter | Less | Pretty\n")
- .append("--- | --- | ---\n")
- .append("*Still* | `renders` | **nicely**\n")
- .append("1 | 2 | 3")
- .toString();
+ new StringBuilder()
+ .append("MarkdownInterpreter | Less | Pretty\n")
+ .append("--- | --- | ---\n")
+ .append("*Still* | `renders` | **nicely**\n")
+ .append("1 | 2 | 3")
+ .toString();
String expected =
- new StringBuilder()
- .append("\n")
- .append(" \n")
- .append(" \n")
- .append(" | MarkdownInterpreter | \n")
- .append(" Less | \n")
- .append(" Pretty | \n")
- .append("
\n")
- .append(" \n")
- .append(" \n")
- .append(" \n")
- .append(" | Still | \n")
- .append(" renders | \n")
- .append(" nicely | \n")
- .append("
\n")
- .append(" \n")
- .append(" | 1 | \n")
- .append(" 2 | \n")
- .append(" 3 | \n")
- .append("
\n")
- .append(" \n")
- .append("
")
- .toString();
+ new StringBuilder()
+ .append("\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | MarkdownInterpreter | \n")
+ .append(" Less | \n")
+ .append(" Pretty | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | Still | \n")
+ .append(" renders | \n")
+ .append(" nicely | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" | 1 | \n")
+ .append(" 2 | \n")
+ .append(" 3 | \n")
+ .append("
\n")
+ .append(" \n")
+ .append("
")
+ .toString();
InterpreterResult result = pegdownInterpreter.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
@@ -264,37 +264,37 @@ public void testSimpleTable() {
public void testAlignedTable() {
String input =
- new StringBuilder()
- .append("| First Header | Second Header | Third Header |\n")
- .append("| :----------- | :-----------: | -------------------: |\n")
- .append("| First row | Data | Very long data entry |\n")
- .append("| Second row | **Cell** | *Cell* |")
- .toString();
+ new StringBuilder()
+ .append("| First Header | Second Header | Third Header |\n")
+ .append("| :----------- | :-----------: | -------------------: |\n")
+ .append("| First row | Data | Very long data entry |\n")
+ .append("| Second row | **Cell** | *Cell* |")
+ .toString();
String expected =
- new StringBuilder()
- .append("\n")
- .append(" \n")
- .append(" \n")
- .append(" | First Header | \n")
- .append(" Second Header | \n")
- .append(" Third Header | \n")
- .append("
\n")
- .append(" \n")
- .append(" \n")
- .append(" \n")
- .append(" | First row | \n")
- .append(" Data | \n")
- .append(" Very long data entry | \n")
- .append("
\n")
- .append(" \n")
- .append(" | Second row | \n")
- .append(" Cell | \n")
- .append(" Cell | \n")
- .append("
\n")
- .append(" \n")
- .append("
")
- .toString();
+ new StringBuilder()
+ .append("\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | First Header | \n")
+ .append(" Second Header | \n")
+ .append(" Third Header | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" \n")
+ .append(" \n")
+ .append(" | First row | \n")
+ .append(" Data | \n")
+ .append(" Very long data entry | \n")
+ .append("
\n")
+ .append(" \n")
+ .append(" | Second row | \n")
+ .append(" Cell | \n")
+ .append(" Cell | \n")
+ .append("
\n")
+ .append(" \n")
+ .append("
")
+ .toString();
InterpreterResult result = pegdownInterpreter.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
From 2b6516cad31b08d12d0f2e76d3bca3a1c8209eb7 Mon Sep 17 00:00:00 2001
From: 1ambda <1amb4a@gmail.com>
Date: Sat, 3 Sep 2016 01:49:06 +0900
Subject: [PATCH 11/18] feat: Support markdown.parser.type attr in md
---
.../zeppelin/markdown/Markdown4jParser.java | 31 +++++++++++
.../markdown/MarkdownInterpreter.java | 40 +++++++++++++--
.../zeppelin/markdown/PegdownParser.java | 14 ++---
.../main/resources/interpreter-setting.json | 11 +++-
.../markdown/Markdown4jParserTest.java | 51 +++++++++++++++++++
.../zeppelin/markdown/PegdownParserTest.java | 50 +++++++++---------
6 files changed, 161 insertions(+), 36 deletions(-)
create mode 100644 markdown/src/main/java/org/apache/zeppelin/markdown/Markdown4jParser.java
create mode 100644 markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown4jParser.java b/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown4jParser.java
new file mode 100644
index 00000000000..0f1780a5b93
--- /dev/null
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown4jParser.java
@@ -0,0 +1,31 @@
+package org.apache.zeppelin.markdown;
+
+import org.markdown4j.Markdown4jProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/** Markdown Parser using markdown4j processor . */
+public class Markdown4jParser implements MarkdownParser {
+ private final Logger logger = LoggerFactory.getLogger(Markdown4jParser.class);
+
+ private Markdown4jProcessor processor;
+
+ public Markdown4jParser() {
+ processor = new Markdown4jProcessor();
+ }
+
+ @Override
+ public String render(String markdownText) {
+ String html = "";
+
+ try {
+ html = processor.process(markdownText);
+ } catch (IOException e) {
+ logger.error("Cannot parse markdown text to HTML using markdown4j", e);
+ }
+
+ return html;
+ }
+}
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
index 84bb7c1e766..d12e0b9566b 100644
--- a/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/MarkdownInterpreter.java
@@ -22,6 +22,7 @@
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
+import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
import org.apache.zeppelin.interpreter.InterpreterUtils;
@@ -33,17 +34,50 @@
/** MarkdownInterpreter interpreter for Zeppelin. */
public class MarkdownInterpreter extends Interpreter {
- private final Logger logger = LoggerFactory.getLogger(MarkdownInterpreter.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(MarkdownInterpreter.class);
private MarkdownParser parser;
+ /** Markdown Parser Type. */
+ public enum MarkdownParserType {
+ PEGDOWN {
+ @Override
+ public String toString() {
+ return PARSER_TYPE_PEGDOWN;
+ }
+ },
+
+ MARKDOWN4j {
+ @Override
+ public String toString() {
+ return PARSER_TYPE_MARKDOWN4J;
+ }
+ }
+ }
+
+ public static final String MARKDOWN_PARSER_TYPE = "markdown.parser.type";
+ public static final String PARSER_TYPE_PEGDOWN = "pegdown";
+ public static final String PARSER_TYPE_MARKDOWN4J = "markdown4j";
+
public MarkdownInterpreter(Properties property) {
super(property);
}
+ public static MarkdownParser createMarkdownParser(String parserType) {
+ LOGGER.debug("Creating " + parserType + " markdown interpreter");
+
+ if (MarkdownParserType.PEGDOWN.toString().equals(parserType)) {
+ return new PegdownParser();
+ } else {
+ /** default parser. */
+ return new Markdown4jParser();
+ }
+ }
+
@Override
public void open() {
- parser = new PegdownParser();
+ String parserType = getProperty(MARKDOWN_PARSER_TYPE);
+ parser = createMarkdownParser(parserType);
}
@Override
@@ -56,7 +90,7 @@ public InterpreterResult interpret(String markdownText, InterpreterContext inter
try {
html = parser.render(markdownText);
} catch (Exception e) {
- logger.error("Exception in MarkdownInterpreter while interpret ", e);
+ LOGGER.error("Exception in MarkdownInterpreter while interpret ", e);
return new InterpreterResult(Code.ERROR, InterpreterUtils.getMostRelevantMessage(e));
}
diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java b/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
index 2236785d6b2..a9e8f7d014b 100644
--- a/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
+++ b/markdown/src/main/java/org/apache/zeppelin/markdown/PegdownParser.java
@@ -5,12 +5,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Pegdown Markdown Parser. */
+/** Markdown Parser using pegdown processor. */
public class PegdownParser implements MarkdownParser {
- private PegDownProcessor processor;
-
private final Logger logger = LoggerFactory.getLogger(PegdownParser.class);
+ private PegDownProcessor processor;
+
public PegdownParser() {
int pegdownOptions = Extensions.ALL_WITH_OPTIONALS - Extensions.ANCHORLINKS;
int parsingTimeoutAsMillis = 5000;
@@ -23,17 +23,19 @@ public String render(String markdownText) {
try {
String parsed = processor.markdownToHtml(markdownText);
- if (null == parsed) throw new RuntimeException("Cannot parse markdown syntax string to HTML");
+ if (null == parsed) {
+ throw new RuntimeException("Cannot parse markdown text to HTML using pegdown");
+ }
html = wrapWithMarkdownClassDiv(parsed);
} catch (RuntimeException e) {
- logger.error("Failed to parsed markdown text", e);
+ logger.error("Cannot parse markdown text to HTML using pegdown", e);
}
return html;
}
- /** wrap with markdown class div to styling DOM using css */
+ /** wrap with markdown class div to styling DOM using css. */
public static String wrapWithMarkdownClassDiv(String html) {
return new StringBuilder()
.append("\n")
diff --git a/markdown/src/main/resources/interpreter-setting.json b/markdown/src/main/resources/interpreter-setting.json
index 1e5d10c31fa..78ad7359117 100644
--- a/markdown/src/main/resources/interpreter-setting.json
+++ b/markdown/src/main/resources/interpreter-setting.json
@@ -2,7 +2,14 @@
{
"group": "md",
"name": "md",
- "className": "org.apache.zeppelin.markdown.Markdown",
- "properties": null
+ "className": "org.apache.zeppelin.markdown.MarkdownInterpreter",
+ "properties": {
+ "markdown.parser.type": {
+ "envName": "MARKDOWN_PARSER_TYPE",
+ "propertyName": "markdown.parser.type",
+ "defaultValue": "markdown4j",
+ "description": "Markdown Parser Type. Available values: markdown4j, pegdown. Default = markdown4j"
+ }
+ }
}
]
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java
new file mode 100644
index 00000000000..6da2757149a
--- /dev/null
+++ b/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zeppelin.markdown;
+
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+
+public class Markdown4jParserTest {
+
+ MarkdownInterpreter md;
+
+ @Before
+ public void setUp() throws Exception {
+ Properties props = new Properties();
+ props.put(MarkdownInterpreter.MARKDOWN_PARSER_TYPE, MarkdownInterpreter.PARSER_TYPE_MARKDOWN4J);
+ md = new MarkdownInterpreter(props);
+ md.open();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ md.close();
+ }
+
+ @Test
+ public void testStrikethrough() {
+ InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
+ assertEquals("
This is deleted text
\n", result.message());
+ }
+}
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
index 9cea9d3921e..66d6d7681a0 100644
--- a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
+++ b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
@@ -29,64 +29,65 @@
public class PegdownParserTest {
- MarkdownInterpreter pegdownInterpreter;
+ MarkdownInterpreter md;
@Before
public void setUp() throws Exception {
- pegdownInterpreter = new MarkdownInterpreter(new Properties());
- pegdownInterpreter.open();
+ Properties props = new Properties();
+ props.put(MarkdownInterpreter.MARKDOWN_PARSER_TYPE, MarkdownInterpreter.PARSER_TYPE_PEGDOWN);
+ md = new MarkdownInterpreter(props);
+ md.open();
}
@After
public void tearDown() throws Exception {
- pegdownInterpreter.close();
+ md.close();
}
@Test
public void testHeader() {
- InterpreterResult r1 = pegdownInterpreter.interpret("# H1", null);
+ InterpreterResult r1 = md.interpret("# H1", null);
assertEquals(wrapWithMarkdownClassDiv("
H1
"), r1.message());
- InterpreterResult r2 = pegdownInterpreter.interpret("## H2", null);
+ InterpreterResult r2 = md.interpret("## H2", null);
assertEquals(wrapWithMarkdownClassDiv("
H2
"), r2.message());
- InterpreterResult r3 = pegdownInterpreter.interpret("### H3", null);
+ InterpreterResult r3 = md.interpret("### H3", null);
assertEquals(wrapWithMarkdownClassDiv("
H3
"), r3.message());
- InterpreterResult r4 = pegdownInterpreter.interpret("#### H4", null);
+ InterpreterResult r4 = md.interpret("#### H4", null);
assertEquals(wrapWithMarkdownClassDiv("
H4
"), r4.message());
- InterpreterResult r5 = pegdownInterpreter.interpret("##### H5", null);
+ InterpreterResult r5 = md.interpret("##### H5", null);
assertEquals(wrapWithMarkdownClassDiv("
H5
"), r5.message());
- InterpreterResult r6 = pegdownInterpreter.interpret("###### H6", null);
+ InterpreterResult r6 = md.interpret("###### H6", null);
assertEquals(wrapWithMarkdownClassDiv("
H6
"), r6.message());
- InterpreterResult r7 = pegdownInterpreter.interpret("Alt-H1\n" + "======", null);
+ InterpreterResult r7 = md.interpret("Alt-H1\n" + "======", null);
assertEquals(wrapWithMarkdownClassDiv("
Alt-H1
"), r7.message());
- InterpreterResult r8 = pegdownInterpreter.interpret("Alt-H2\n" + "------", null);
+ InterpreterResult r8 = md.interpret("Alt-H2\n" + "------", null);
assertEquals(wrapWithMarkdownClassDiv("
Alt-H2
"), r8.message());
}
@Test
public void testStrikethrough() {
- InterpreterResult result = pegdownInterpreter.interpret("This is ~~deleted~~ text", null);
+ InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
assertEquals(
wrapWithMarkdownClassDiv("
This is deleted text
"), result.message());
}
@Test
public void testItalics() {
- InterpreterResult result = pegdownInterpreter.interpret("This is *italics* text", null);
+ InterpreterResult result = md.interpret("This is *italics* text", null);
assertEquals(
wrapWithMarkdownClassDiv("
This is italics text
"), result.message());
}
@Test
public void testStrongEmphasis() {
- InterpreterResult result =
- pegdownInterpreter.interpret("This is **strong emphasis** text", null);
+ InterpreterResult result = md.interpret("This is **strong emphasis** text", null);
assertEquals(
wrapWithMarkdownClassDiv("
This is strong emphasis text
"),
result.message());
@@ -108,7 +109,7 @@ public void testOrderedList() {
.append("")
.toString();
- InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ InterpreterResult result = md.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
}
@@ -130,7 +131,7 @@ public void testUnorderedList() {
.append("")
.toString();
- InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ InterpreterResult result = md.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
}
@@ -181,14 +182,13 @@ public void testLinks() {
.append("
Some text to show that the reference links can follow later.
")
.toString();
- InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ InterpreterResult result = md.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
}
@Test
public void testInlineCode() {
- InterpreterResult result =
- pegdownInterpreter.interpret("Inline `code` has `back-ticks around` it.", null);
+ InterpreterResult result = md.interpret("Inline `code` has `back-ticks around` it.", null);
assertEquals(
wrapWithMarkdownClassDiv(
"
Inline code has back-ticks around it.
"),
@@ -198,7 +198,7 @@ public void testInlineCode() {
@Test
public void testBlockQuotes() {
InterpreterResult r1 =
- pegdownInterpreter.interpret(
+ md.interpret(
"> Blockquotes are very handy in email to emulate reply text.\n"
+ "> This line is part of the same quote.",
null);
@@ -210,7 +210,7 @@ public void testBlockQuotes() {
r1.message());
InterpreterResult r2 =
- pegdownInterpreter.interpret(
+ md.interpret(
"> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **MarkdownInterpreter** into a blockquote. ",
null);
assertEquals(
@@ -256,7 +256,7 @@ public void testSimpleTable() {
.append("")
.toString();
- InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ InterpreterResult result = md.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
}
@@ -296,7 +296,7 @@ public void testAlignedTable() {
.append("")
.toString();
- InterpreterResult result = pegdownInterpreter.interpret(input, null);
+ InterpreterResult result = md.interpret(input, null);
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
}
}
From d228423d636c2d7d9ca4d8c7b7c211433c5c0706 Mon Sep 17 00:00:00 2001
From: 1ambda <1amb4a@gmail.com>
Date: Sun, 4 Sep 2016 13:33:49 +0900
Subject: [PATCH 12/18] docs: Update markdown docs config, examples
---
.../markdown-example-markdown4j-parser.png | Bin 0 -> 264309 bytes
.../markdown-example-pegdown-parser.png | Bin 0 -> 147514 bytes
.../docs-img/markdown-interpreter-setting.png | Bin 32571 -> 67890 bytes
docs/interpreter/markdown.md | 34 ++++++++++++++++--
4 files changed, 32 insertions(+), 2 deletions(-)
create mode 100644 docs/assets/themes/zeppelin/img/docs-img/markdown-example-markdown4j-parser.png
create mode 100644 docs/assets/themes/zeppelin/img/docs-img/markdown-example-pegdown-parser.png
diff --git a/docs/assets/themes/zeppelin/img/docs-img/markdown-example-markdown4j-parser.png b/docs/assets/themes/zeppelin/img/docs-img/markdown-example-markdown4j-parser.png
new file mode 100644
index 0000000000000000000000000000000000000000..e3455e31ce3ee96ef822c27e51d25556f889746c
GIT binary patch
literal 264309
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
zve