diff --git a/par/md.test.ts b/par/md.test.ts
index 0325f83..2349623 100644
--- a/par/md.test.ts
+++ b/par/md.test.ts
@@ -4,6 +4,16 @@ import { MarkdownParser } from "./md.ts";
const ren = new HtmlStrRenderer();
+Deno.test({
+ name: "should skip new line character",
+ fn: () => {
+ const par = new MarkdownParser();
+ assertEquals(ren.render(par.parse("\n")), "");
+ assertEquals(ren.render(par.parse("\r\n")), "");
+ assertEquals(ren.render(par.parse("\n\r\n")), "");
+ },
+});
+
Deno.test({
name: "should parse empty ATX header",
fn: () => {
@@ -42,6 +52,8 @@ Deno.test({
assertEquals(ren.render(par.parse(" # hello")), "
hello
");
assertEquals(ren.render(par.parse(" # hello")), "hello
");
assertEquals(ren.render(par.parse(" # hello")), "hello
");
+ assertEquals(ren.render(par.parse("\n # hello")), "hello
");
+ assertEquals(ren.render(par.parse("\r\n # hello")), "hello
");
},
});
@@ -56,3 +68,21 @@ Deno.test({
assertEquals(ren.render(par.parse("###### hello #")), "hello
");
},
});
+
+Deno.test({
+ name: "should parse many headers with text",
+ fn: () => {
+ const par = new MarkdownParser();
+
+ const input = `\
+# hello
+## world
+### this is
+#### my world!`;
+
+ assertEquals(
+ ren.render(par.parse(input)),
+ "hello
world
this is
my world!
",
+ );
+ },
+});
diff --git a/par/md.ts b/par/md.ts
index b41bcd3..c660f13 100644
--- a/par/md.ts
+++ b/par/md.ts
@@ -2,6 +2,8 @@ import { AnyNode, Elem, Fragment, TextNode } from "../core/node.ts";
import { isNil } from "../core/utils.ts";
import { Parser } from "./types.ts";
+const RE_NEW_LINE = /^\r?\n/;
+
const RE_OPEN_ATX_HEADING = /^\s{0,3}(#{1,6})(\s|$)/;
const RE_CLOSE_ATX_HEADING = /(^|\s+)#*\s*$/;
@@ -11,33 +13,50 @@ export class MarkdownParser implements Parser {
let readStr = input;
- const match = RE_OPEN_ATX_HEADING.exec(readStr);
- if (!isNil(match)) {
- readStr = readStr.slice(match[0].length);
-
- const atxHeading: AstAtxHeading = {
- kind: AstKind.AtxHeading,
- level: match[1].length as HeadingLevel,
- content: [],
- };
- ast.content.push(atxHeading);
-
- if (match[2].length > 0) {
- const endMatch = RE_CLOSE_ATX_HEADING.exec(readStr);
-
- const headingContent = isNil(endMatch)
- ? readStr.split("\n", 1)[0]
- : readStr.slice(0, endMatch.index);
- readStr = readStr.slice(headingContent.length);
-
- if (headingContent.trim().length) {
- const text: AstText = {
- kind: AstKind.Text,
- content: headingContent.trim(),
- };
- atxHeading.content.push(text);
+ while (readStr.trim().length) {
+ {
+ // 1. clear new line character
+ const match = RE_NEW_LINE.exec(readStr);
+ if (!isNil(match)) {
+ readStr = readStr.slice(match[0].length);
}
}
+
+ // 2. try to find atx heading sequence
+ const match = RE_OPEN_ATX_HEADING.exec(readStr);
+ if (!isNil(match)) {
+ readStr = readStr.slice(match[0].length);
+
+ const atxHeading: AstAtxHeading = {
+ kind: AstKind.AtxHeading,
+ level: match[1].length as HeadingLevel,
+ content: [],
+ };
+ ast.content.push(atxHeading);
+
+ if (match[2].length > 0) {
+ const endMatch = RE_CLOSE_ATX_HEADING.exec(readStr);
+
+ const headingContent = !isNil(endMatch)
+ ? readStr.slice(0, endMatch.index)
+ : readStr.includes("\n")
+ ? readStr.slice(0, readStr.indexOf("\n") + 1)
+ : readStr;
+ readStr = readStr.slice(
+ headingContent.length + (endMatch?.[0].length ?? 0),
+ );
+
+ if (headingContent.length) {
+ const text: AstText = {
+ kind: AstKind.Text,
+ content: headingContent.trim(),
+ };
+ atxHeading.content.push(text);
+ }
+ }
+ } else {
+ break;
+ }
}
return new Fragment(ast.content.map(Heading));