Something went wrong. Try again.
personal knowledge base
djot markdown knowledge wiki
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210(local util (require :lua.util))
(local indent-level " ")
(fn copy-range [t from to] (assert (util.array? t) (.. "Table is not an array" (_G.fennel.view t))) (let [r {}] (for [idx from to] (tset r (+ 1 (- idx from)) (. t idx))) r))
(lambda escape-html [writer str] (for [idx 1 (length str)] (case (str:byte idx) (where [b] (= b (str:byte "&"))) (writer:write "&") (where [b] (= b (str:byte "<"))) (writer:write "<") (where [b] (= b (str:byte ">"))) (writer:write ">") (where [b] (= b (str:byte "\""))) (writer:write """) (where [b] (= b (str:byte "'"))) (writer:write "'") _ (writer:write-byte (string.byte str idx)))))
(lambda write-level [writer level] (for [_idx 1 level] (writer:write indent-level)) )
(fn render-attrs [writer attrs] (if attrs (each [key value (pairs attrs)] (writer:write " ") (writer:write key) (writer:write "=\"") ; TODO should attrs be escaped differently? (escape-html writer (tostring value)) (writer:write "\""))))
(local render-containers {:doc {:kind :function :begin (fn [_writer _attrs]) :end (fn [_writer _attrs])} :heading {:kind :function :begin (fn [writer attrs] (writer:write "<h") (writer:write (tostring attrs.level)) (render-attrs writer attrs) (writer:write ">")) :end (fn [writer attrs] (writer:write "</h") (writer:write (tostring attrs.level)) (writer:write ">"))} :link {:kind :function-inline :begin (fn [writer attrs] (writer:write "<a") (if attrs (do (set attrs.href attrs.target) (set attrs.target nil) (render-attrs writer attrs))) (writer:write ">")) :end (fn [writer _attrs] (writer:write "</a>"))} :code-block {:kind :function :begin (fn [writer attrs] (writer:write "<pre><code") (render-attrs writer attrs) (writer:write ">")) :end (fn [writer _attrs] (writer:write "</code></pre>"))} :unordered-list {:kind :function :begin (fn [writer attrs] (writer:write "<ul") (render-attrs writer attrs) (writer:write ">")) :end (fn [writer _attrs] (writer:write "</ul>"))} :task-list {:kind :function :begin (fn [writer attrs] (writer:write "<ul") (if attrs (set attrs.class "task-list")) (render-attrs writer attrs) (writer:write ">")) :end (fn [writer _attrs] (writer:write "</ul>"))} :list {:kind :function :begin (fn [writer attrs] (writer:write "<ul") (render-attrs writer attrs) (writer:write ">")) :end (fn [writer _attrs] (writer:write "</ul>"))} :list-item {:kind :function :begin (fn [writer attrs] (writer:write "<li") (render-attrs writer attrs) (writer:write ">") (if (and attrs attrs.checkbox) (writer:write (.. "<input disabled=\"\" type=\"checkbox\"" (case attrs.checkbox "checked" " checked" "unchecked" "" _ (error (.. "Unknown check state: " attrs.checkbox))) ">")))) :end (fn [writer _attrs] (writer:write "</li>"))} :right-single-quote {:kind "plain" :plain "‘"} :left-single-quote {:kind "plain" :plain "’"} :left-double-quote {:kind "plain" :plain "“"} :right-double-quote {:kind "plain" :plain "”"} :paragraph {:kind :function :begin (fn [writer attrs] (writer:write "<p") (render-attrs writer attrs) (writer:write ">")) :end (fn [writer _attrs] (writer:write "</p>"))} :section {:kind :function :begin (fn [writer attrs] (writer:write "<section") (render-attrs writer attrs) (writer:write ">")) :end (fn [writer _attrs] (writer:write "</section>"))} :blankline {:kind "plain" :plain "\n"} :softbreak {:kind "plain" :plain "\n"}})
(var render-node (fn [] (error "Unset!")))
(fn render-container [state container attrs children] (let [renderer (. render-containers container)] (if renderer (case renderer.kind :none nil :plain (do (assert (or (not attrs) (= 0 (length attrs))) (.. "Plain renderer should not have attributes: " (_G.fennel.view container attrs))) (assert (or (not attrs) (= 0 (length children))) (.. "Plain renderer should not have children: " (_G.fennel.view container children))) (state.writer:write renderer.plain)) :function (do (write-level state.writer state.level) (renderer.begin state.writer attrs) (state.writer:write "\n") (each [_ child (ipairs children)] (render-node {:writer state.writer :level (+ 1 state.level)} child)) (state.writer:write "\n") (write-level state.writer state.level) (renderer.end state.writer attrs) (state.writer:write "\n")) :function-inline (do (renderer.begin state.writer attrs) (each [_ child (ipairs children)] (render-node state child)) (renderer.end state.writer attrs))) ;; if container begins with : it is an escaped string ;; render it literally (if (= (string.byte container 1) (string.byte ":")) (do (state.writer:write (string.sub container 2)) (assert (= 0 (length children)) (.. "Children for literal escapes are unsupported" (_G.fennel.view container) ": " (_G.fennel.view children)))) (do (write-level state.writer state.level) (state.writer:write "<") (assert (util.string? container)) (state.writer:write container) (render-attrs state.writer attrs) (state.writer:write ">\n") (each [_ child (ipairs children)] (render-node {:writer state.writer :level (+ 1 state.level)} child)) (write-level state.writer state.level) (state.writer:write "</") (state.writer:write container) (state.writer:write ">\n"))))))
(set render-node (lambda [state node] (case (type node) "string" (escape-html state.writer node) "number" (do (write-level state.writer state.level) (state.writer:write (tostring node)) (state.writer:write "\n")) ;; If the first element is not a string, render them as a sequence "table" (if (not (util.string? (. node 1))) (each [_ node- (ipairs node)] (render-node state node-)) (let [container (. node 1) attrs (if (util.map? (. node 2)) (. node 2) nil) children (if attrs (copy-range node 3 (length node)) (copy-range node 2 (length node)))] (render-container state container attrs children))) else (error (.. "Unknown type for rendering " else)))))
(lambda render [writer ast] (render-node {:level 0 :writer writer} ast))
render