blob: 436b7829e73879256bcb8628cfe70ebb5d27a531 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
(ns clarktown.core
(:require
[clojure.string :as string]
[clarktown.parsers.empty-block :as empty-block]
[clarktown.parsers.heading-block :as heading-block]))
(def default-parsers
[{:matcher empty-block/is?
:renderers [empty-block/render]}
{:matcher heading-block/is?
:renderers [heading-block/render]}])
(defn find-parser-by-block
"Find a parser from `parsers` that matches the given `block`."
[parsers block]
(->> parsers
(filter
(fn [{:keys [matcher]}]
(matcher block)))
first))
(defn parse-block-with-known-parser
"Parses a given `block` with a known `parser`."
[parser block]
(loop [block block
renderers (:renderers parser)]
(if (empty? renderers)
block
(let [renderer (first renderers)]
(recur (renderer block)
(drop 1 renderers))))))
(defn- parse-block-with-unknown-parsers
"Parses the given `block` with all the parsers that do not have
a matcher function, useful for any fallback parsing one might want
to do."
[parsers block]
(loop [block block
parsers (filter #(= nil (:matcher %)) parsers)]
(if (empty? parsers)
block
(loop [block block
renderers (:renderers (first parsers))]
(if (empty? renderers)
block
(let [renderer (first renderers)]
(recur (renderer block)
(drop 1 renderers))))))))
(defn- parse-blocks
"Parses each individual Markdown block, given as `blocks`, with
the list of `parsers`."
[blocks parsers]
(for [block blocks]
(if-let [parser (find-parser-by-block parsers block)]
(parse-block-with-known-parser parser block)
(parse-block-with-unknown-parsers parsers block))))
(defn- parse
"Parses given `markdown` with `parsers`."
[markdown parsers]
(let [blocks (string/split markdown #"\n\n")
parsed-blocks (parse-blocks blocks parsers)]
(string/join "" parsed-blocks)))
(defn render
"Renders the given `markdown` into a consumable HTML form. Optionally,
a second argument can be passed that is made out of a vector of parsers.
A parser is a map that consists of two things;
- A matcher (optional) , which returns a truthy or falsey value
- Renderers, which will be run on the a block when matcher returns true.
There can be any number of renderers.
Each matcher, and each renderer have to be a function that take a single
argument, which is a given Markdown block.
An example parser:
```
{:matcher (fn [block] ...)
:renderers [(fn [block] ...) (fn [block] ...)]}
```"
([markdown]
(render markdown default-parsers))
([markdown parsers]
(parse markdown parsers)))
(render (slurp "/Users/asko/Documents/work/clarktown/test.md"))
|