par(md): add loop...
... to allow parse many headers
This commit is contained in:
parent
5bd148bf17
commit
328343ca76
2 changed files with 74 additions and 25 deletions
|
@ -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")), "<h1>hello</h1>");
|
||||
assertEquals(ren.render(par.parse(" # hello")), "<h1>hello</h1>");
|
||||
assertEquals(ren.render(par.parse(" # hello")), "<h1>hello</h1>");
|
||||
assertEquals(ren.render(par.parse("\n # hello")), "<h1>hello</h1>");
|
||||
assertEquals(ren.render(par.parse("\r\n # hello")), "<h1>hello</h1>");
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -56,3 +68,21 @@ Deno.test({
|
|||
assertEquals(ren.render(par.parse("###### hello #")), "<h6>hello</h6>");
|
||||
},
|
||||
});
|
||||
|
||||
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)),
|
||||
"<h1>hello</h1><h2>world</h2><h3>this is</h3><h4>my world!</h4>",
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
69
par/md.ts
69
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));
|
||||
|
|
Reference in a new issue