-
-
Notifications
You must be signed in to change notification settings - Fork 121
/
core.clj
129 lines (118 loc) · 5.01 KB
/
core.clj
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
(ns markdown.core
(:require [clojure.java.io :as io]
[clojure.string :as string]
[markdown.common
:refer [*substring* *inhibit-separator*]]
[markdown.links
:refer [parse-reference parse-reference-link parse-footnote-link]]
[markdown.transformers
:refer [transformer-vector footer parse-metadata-headers]])
(:import [java.io BufferedReader
BufferedWriter
StringReader
StringWriter
Writer]))
(defn- write [^Writer writer ^String text]
(doseq [c text] (.write writer (int c))))
(defn- init-transformer [writer {:keys [replacement-transformers custom-transformers inhibit-separator]}]
(fn [line next-line state]
(binding [*inhibit-separator* inhibit-separator]
(let [[text new-state]
(reduce
(fn [[text, state] transformer]
(transformer text (assoc state :next-line next-line)))
[line state]
(or replacement-transformers
(into transformer-vector custom-transformers)))]
(write writer text)
new-state))))
(defn parse-references [in]
(let [references (atom {})]
(if (instance? StringReader in)
(do
(doseq [line (line-seq (io/reader in))]
(parse-reference-link line references))
(.reset in))
(doseq [line (line-seq (io/reader in))]
(parse-reference-link line references)))
@references))
(defn parse-footnotes [in]
(let [footnotes (atom {:next-fn-id 1 :processed {} :unprocessed {}})]
(if (instance? StringReader in)
(do
(doseq [line (line-seq (io/reader in))]
(parse-footnote-link line footnotes))
(.reset in))
(doseq [line (line-seq (io/reader in))]
(parse-footnote-link line footnotes)))
@footnotes))
(defn parse-metadata [in]
(let [lines (line-seq (io/reader in))
metadata (parse-metadata-headers lines)]
(when (instance? StringReader in)
(.reset in))
metadata))
(defn parse-params
[params]
(when (not= 0 (mod (count params) 2))
(throw (IllegalArgumentException.
"Must supply an even number of parameters")))
(when params (apply assoc {} params)))
(defn md-to-html
"reads markdown content from the input stream and writes HTML to the provided
output stream. If metadata was requested to be parsed it is returned, otherwise
nil is returned."
[in out & params]
(binding [markdown.common/*substring* (fn [^String s n] (.substring s n))
markdown.transformers/*formatter* clojure.core/format]
(let [params (parse-params params)
references (when (:reference-links? params) (parse-references in))
footnotes (when (:footnotes? params) (parse-footnotes in))
metadata (when (:parse-meta? params) (parse-metadata in))]
(with-open [^BufferedReader rdr (io/reader in)
^BufferedWriter wrt (io/writer out)]
(when (and metadata (:parse-meta? params))
(while (not= "" (string/trim (.readLine rdr)))))
(let [transformer (init-transformer wrt params)]
(loop [^String line (.readLine rdr)
^String next-line (.readLine rdr)
state (merge {:last-line-empty? true
:references references
:footnotes footnotes}
params)]
(let [line (if (:skip-next-line? state) "" line)
buf (:buf state)
state (if buf
(transformer buf
(:next-line state)
(-> state
(dissoc :buf :lists :next-line)
(assoc :last-line-empty? true)))
state)]
(if line
(recur next-line
(.readLine rdr)
(assoc (transformer line next-line (dissoc state :skip-next-line?))
:last-line-empty? (empty? (.trim line))))
(transformer (footer (:footnotes state)) nil (assoc state :eof true))))))
(.flush wrt)
metadata))))
(defn md-to-html-string*
"converts a markdown formatted string to an HTML formatted string"
[text params]
(when text
(let [input (new StringReader text)
output (new StringWriter)
metadata (apply md-to-html input output params)
html (.toString output)]
{:metadata metadata :html html})))
(defn md-to-meta
"converts a markdown formatted string to a metadata map"
[text]
(when text
(let [input (new StringReader text)]
(parse-metadata input))))
(defn md-to-html-string [text & params]
(:html (md-to-html-string* text params)))
(defn md-to-html-string-with-meta [text & params]
(md-to-html-string* text (into [:parse-meta? true] params)))