forked from osmarks/xenotime
87 lines
3.6 KiB
Nim
87 lines
3.6 KiB
Nim
import cmark/native as cmark except Node, Parser
|
|
# the builtin re library would probably be better for this - it can directly take cstrings (so better perf when dealing with the cstrings from cmark) and may be faster
|
|
# unfortunately it does not expose a findAll thing which returns the *positions* of everything for some weird reason
|
|
|
|
cmark_gfm_core_extensions_ensure_registered()
|
|
|
|
type
|
|
Node = object
|
|
raw: NodePtr
|
|
BorrowedNode = object
|
|
raw: NodePtr
|
|
Parser = object
|
|
raw: ParserPtr
|
|
|
|
proc `=copy`(dest: var Node, source: Node) {.error.}
|
|
proc `=destroy`(x: var Node) = cmark_node_free(x.raw)
|
|
proc `=destroy`(x: var BorrowedNode) = discard
|
|
|
|
proc `=destroy`(x: var Parser) = cmark_parser_free(x.raw)
|
|
|
|
proc borrow(n: Node): BorrowedNode = BorrowedNode(raw: n.raw)
|
|
|
|
proc newParser(options: int64, extensions: seq[string]): Parser =
|
|
let parser: ParserPtr = cmark_parser_new(options.cint)
|
|
if parser == nil: raise newException(CatchableError, "failed to initialize parser")
|
|
# load and enable desired syntax extensions
|
|
# these are freed with the parser (probably)
|
|
for ext in extensions:
|
|
let e: cstring = ext
|
|
let eptr = cmark_find_syntax_extension(e)
|
|
if eptr == nil:
|
|
cmark_parser_free(parser)
|
|
raise newException(LibraryError, "failed to find extension " & ext)
|
|
if cmark_parser_attach_syntax_extension(parser, eptr) == 0:
|
|
cmark_parser_free(parser)
|
|
raise newException(CatchableError, "failed to attach extension " & ext)
|
|
Parser(raw: parser)
|
|
|
|
proc parse(p: Parser, document: string): Node =
|
|
let
|
|
str: cstring = document
|
|
length = len(document).csize_t
|
|
cmark_parser_feed(p.raw, str, length)
|
|
let ast = cmark_parser_finish(p.raw)
|
|
if ast == nil: raise newException(CatchableError, "parsing failed - should not occur")
|
|
Node(raw: ast)
|
|
|
|
proc nodeType(n: BorrowedNode): NodeType = cmark_node_get_type(n.raw)
|
|
proc nodeContent(n: BorrowedNode): string = $cmark_node_get_literal(n.raw)
|
|
|
|
proc newNode(ty: NodeType, content: string): Node =
|
|
let raw = cmark_node_new(ty)
|
|
if raw == nil: raise newException(CatchableError, "node creation failed")
|
|
if cmark_node_set_literal(raw, content) != 1:
|
|
cmark_node_free(raw)
|
|
raise newException(CatchableError, "node content setting failed")
|
|
Node(raw: raw)
|
|
|
|
proc parentNode(parentOf: BorrowedNode): BorrowedNode = BorrowedNode(raw: cmark_node_parent(parentOf.raw))
|
|
proc pushNodeAfter(after: BorrowedNode, node: sink Node) {.nodestroy.} = assert cmark_node_insert_before(after.raw, node.raw) == 1
|
|
proc unlinkNode(node: sink BorrowedNode): Node {.nodestroy.} =
|
|
cmark_node_unlink(node.raw)
|
|
Node(raw: node.raw)
|
|
|
|
proc render(ast: Node, options: int64, parser: Parser): string =
|
|
let html: cstring = cmark_render_html(ast.raw, options.cint, cmark_parser_get_syntax_extensions(parser.raw))
|
|
defer: free(html)
|
|
result = $html
|
|
|
|
iterator cmarkTree(root: BorrowedNode): (EventType, BorrowedNode) {.inline.} =
|
|
var iter = cmark_iter_new(root.raw)
|
|
if iter == nil: raise newException(CatchableError, "iterator initialization failed")
|
|
defer: cmark_iter_free(iter)
|
|
while true:
|
|
let ev = cmark_iter_next(iter)
|
|
if ev == etDone: break
|
|
let node: NodePtr = cmark_iter_get_node(iter)
|
|
yield (ev, BorrowedNode(raw: node))
|
|
|
|
proc renderToHtml*(input: string): string =
|
|
let opt = CMARK_OPT_STRIKETHROUGH_DOUBLE_TILDE or CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES
|
|
|
|
# initialize parser with the extensions in use, parse things
|
|
let parser = newParser(opt, @["table", "strikethrough"])
|
|
let doc = parse(parser, input)
|
|
|
|
render(doc, opt, parser) |