Skip to content

Commit 60601e2

Browse files
committed
Handle YAML front matter | hr
Signed-off-by: George Lemon <georgelemon@protonmail.com>
1 parent 0e225c7 commit 60601e2

File tree

4 files changed

+54
-7
lines changed

4 files changed

+54
-7
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
1818
## 😍 Key Features
1919
- [x] Extremely Fast & Lightweight! [Check benchmarks](#benchmarks)
20-
- [x] Compiled CLI application
20+
- [x] Compiled cross-platform CLI app
2121
- [x] Nim library for easy integration in your 👑 Nim projects
2222
- [x] Addon for Node.js JavaScript runtime via N-API
2323
- [x] Markdown to HTML
24-
- [ ] Auto-generate Table of Contents (ToC)
24+
- [x] Auto-generate Table of Contents (ToC)
2525
- [x] Auto-generate heading IDs for anchor links
2626
- [ ] Markdown to PDF
2727
- [ ] Markdown to JSON (structured data)

marvdown.nimble

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ requires "nim >= 2.0.0"
1515
requires "kapsis#head"
1616
requires "denim#head"
1717
requires "jsony#head"
18+
requires "nyml#head"
1819

1920
task napi, "Build Marvdown as a Node.js addon using N-API":
2021
exec "denim build src/marvdown.nim --cmake -y"

src/marvdown/lexer.nim

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,25 @@ proc nextToken*(lex: var MarkdownLexer): MarkdownTokenTuple =
197197
lex.advance()
198198

199199
if count >= 3 and (lex.current == '\n' or lex.current == '\0'):
200-
# Horizontal rule
201-
return newTokenTuple(lex, mtkHorizontalRule, repeat(ch, count), wsno=lex.wsno)
200+
# Horizontal rule, or the begining of a YAML front matter
201+
if lex.line == 1:
202+
# YAML front matter detected
203+
lex.strbuf.setLen(0)
204+
while true:
205+
if lex.current == '\0':
206+
break
207+
if lex.current == '-' and lex.peek() == '-' and lex.peek(2) == '-':
208+
# End of front matter
209+
lex.advance(); lex.advance(); lex.advance()
210+
if lex.current in {'\n', '\r'}:
211+
lex.advance()
212+
break
213+
lex.strbuf.add(lex.current)
214+
lex.advance()
215+
let frontMatter = lex.strbuf.strip()
216+
return newTokenTuple(lex, mtkDocument, frontMatter, wsno=lex.wsno)
217+
else:
218+
return newTokenTuple(lex, mtkHorizontalRule, repeat(ch, count), wsno=lex.wsno)
202219

203220
if (ch in {'-', '*', '+'}) and (lex.current == ' ' or lex.current == '\t'):
204221
# Unordered list item

src/marvdown/parser.nim

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import htmlparser {.all.}
1111
export HtmlTag
1212

1313
import ./lexer, ./ast
14-
import pkg/jsony
14+
import pkg/[jsony, nyml]
1515

1616
type
1717
MarkdownParser* = object
@@ -32,6 +32,8 @@ type
3232
## Internal: Counter for generating unique selectors
3333
ast*: seq[MarkdownNode]
3434
## The abstract syntax tree (AST) of the parsed markdown document
35+
headerYaml: JsonNode
36+
## Parsed YAML front matter as JsonNode
3537
footnotes: OrderedTableRef[string, MarkdownNode]
3638
## Footnote definitions parsed from the document
3739
footnotesHtml*: string
@@ -655,6 +657,28 @@ proc parseMarkdown(md: var Markdown, currentParagraph: var MarkdownNode) =
655657
closeCurrentParagraph()
656658
let bqNode = md.parseBlockquote()
657659
md.ast.add(bqNode)
660+
of mtkHorizontalRule:
661+
closeCurrentParagraph()
662+
md.ast.add(MarkdownNode(
663+
kind: mdkHorizontalRule,
664+
line: curr.line,
665+
wsno: curr.wsno
666+
))
667+
md.advance()
668+
of mtkDocument:
669+
try:
670+
# Parse YAML front matter
671+
# TODO test YAML parsing (https://github.com/openpeeps/nyml)
672+
md.headerYaml = fromYaml(curr.token, JsonNode)
673+
except YAMLException as e:
674+
# On error, add a text node with the error message
675+
md.ast.add(MarkdownNode(
676+
kind: mdkText,
677+
text: curr.token, # invalid YAML, just add as text
678+
line: curr.line,
679+
wsno: curr.wsno
680+
))
681+
md.advance()
658682
of mtkFootnoteRef:
659683
withCurrentParagraph do:
660684
let id = curr.attrs.get()[0]
@@ -673,7 +697,6 @@ proc parseMarkdown(md: var Markdown, currentParagraph: var MarkdownNode) =
673697
md.advance()
674698
else:
675699
closeCurrentParagraph()
676-
md.advance()
677700

678701
proc newMarkdown*(content: sink string,
679702
opts: MarkdownOptions = defaultOptions): Markdown =
@@ -712,6 +735,10 @@ proc hasSelectors*(md: Markdown): bool =
712735
## Check if there are any headline selectors (anchors) in the parsed Markdown
713736
md.selectors != nil and md.selectors.len > 0
714737

738+
proc getHeader*(md: Markdown): JsonNode =
739+
## Get the parsed YAML front matter from the Markdown
740+
md.headerYaml
741+
715742
proc getFootnotes*(md: Markdown): OrderedTableRef[string, MarkdownNode] =
716743
## Get the footnote definitions from the parsed Markdown
717744
md.footnotes
@@ -867,5 +894,7 @@ proc renderNode(md: var Markdown, node: MarkdownNode): string =
867894
fnContent
868895
)
869896
)
897+
of mdkHorizontalRule: result = "<hr>"
870898
else:
871-
discard
899+
echo node.kind
900+
echo "Warning: Unhandled MarkdownNode kind in renderNode"

0 commit comments

Comments
 (0)