/ render.fnl
render.fnl
1 (local fennel (require :fennel)) 2 (fn _G.pp [x] (print (fennel.view x))) 3 4 (var footnote-counter 1) 5 6 (fn render-contents [contents] 7 (var rendered "<p>") 8 (each [_ line (ipairs contents)] 9 (_G.pp line) 10 (match (. line :type) 11 "raw-text" (set rendered (.. rendered line.text "\n")) 12 "Italics" (set rendered (.. rendered "<em>" line.text "</em>\n")) 13 "Bold" (set rendered (.. rendered "<b>" line.text "</b>\n")) 14 "Strikethrough" (set rendered (.. rendered "<del>" line.text "</del>\n")) 15 "Monospace" (set rendered (.. rendered "<code>" line.text "</code>\n")) 16 "Link" (set rendered (.. rendered "<a href=\"" line.destination "\" class=\"" line.class "\">" line.title "</a>\n")) 17 "Footnote-Link" (do 18 (set rendered (.. rendered "<sup>\n<a href=fn:" (tostring footnote-counter) " id=fnref:" (tostring footnote-counter) ">[" (tostring footnote-counter) "]</a>\n</sup>\n")) 19 (set footnote-counter (+ footnote-counter 1))))) 20 (.. rendered "</p>")) 21 22 (local render-grammar 23 {:Header (fn render-header [{: contents : size}] 24 (let [header-type (tostring size) 25 header (.. "h" header-type) 26 contents-render (render-contents contents)] 27 (.. "<" header ">\n" contents-render "</" header ">\n"))) 28 :List (fn render-list [{: elements}] 29 (var list-str "<ul>\n") 30 (var depth 0) 31 (each [_ element (ipairs elements)] 32 (_G.pp element) 33 (let [contents-render (render-contents (. element :contents)) 34 indent-level (. element :indent-level)] 35 (_G.pp (.. "indent-level " indent-level " depth: " depth)) 36 (if (= indent-level depth) ; If we're at the right indent level 37 (set list-str (.. list-str "<li>" contents-render "</li>\n")) ; Just add the string as a list element 38 (do ; Otherwise 39 (while (not (= depth indent-level)) ; While our depth level is different from the indent level 40 (if (> indent-level depth) ; If we need to increase depth 41 (do 42 (set list-str (.. list-str "<ul>\n")) ; Add another layer in the render 43 (set depth (+ depth 1))) ; Increase our depth level 44 (do ; We need to decrease depth 45 (set list-str (.. list-str "</ul>\n")) ; Close a layer 46 (set depth (- depth 1))))) ; Reduce depth level 47 (set list-str (.. list-str "<li>" contents-render "</li>\n")))))) 48 (while (not (= depth 0)) 49 (set list-str (.. list-str "</ul>\n")) 50 (set depth (- depth 1)) 51 (_G.pp (.. "depth: " depth))) 52 (set list-str (.. list-str "</ul>\n")) ; Close the first one 53 list-str) 54 :Quote (fn render-quote [{: contents}] 55 (let [contents-render (render-contents contents)] 56 (.. "<blockquote>\n" contents-render "</blockquote>\n"))) 57 :Paragraph (fn render-paragraph [{: contents}] 58 (render-contents contents)) ; Contents render handles the <p> tag? 59 :Image (fn render-image [{: link : contents}] 60 (.. "<img\n src=" link "\n alt=" (render-contents contents) " />")) ; TODO this fucks up the alt tag 61 ; Tags are a type of metadata 62 :Tag (fn metadata-tag [{: tags} metadata] 63 (if (= metadata.tags nil) 64 (tset metadata "tags" [tags]) 65 (table.insert metadata.tags tags)) 66 metadata) 67 :Metadata (fn metadata [{: key : val} metadata] 68 (if (= metadata.key nil) 69 (tset metadata key [val]) 70 (table.insert metadata.key val)) 71 metadata) 72 ; Not sure what to do about code either 73 ; Need to look into pygments or whatever see how that works 74 ; Or find some sort of minimal js solution? 75 ; Not really sure 76 :Code (fn code [{: codehint : contents}] 77 (.. "<code>\n" contents "</code>\n")) 78 ; Each footnote gets an incremental ID 79 ; the contents parser will link to the appropriate footnote 80 ; Don't forget to link to the ref (back link) 81 ; See seirdy's footnotes for an example 82 ; https://seirdy.one/meta/site-design/#fn:1 83 :Footnote (fn footnotes [{: elements}] 84 (var count 1) 85 (var rendered "<ol>\n") 86 (each [_ element (ipairs elements)] 87 (let [contents-render (render-contents element.contents)] 88 (set rendered (.. rendered "<li id=fn:" count ">\n")) 89 (set rendered (.. rendered contents-render "</li>\n")) 90 (set rendered (.. rendered "<a href=\"#fnref:" count "\">Back</a>\n")) 91 (set count (+ count 1)))) 92 (.. rendered "</ol>\n"))}) 93 94 (fn render [blocks grammar] 95 (var rendered "") 96 (var metadata {}) 97 (each [_ section (ipairs blocks)] 98 (each [_ block (ipairs section)] 99 (let [rule-set (. grammar (. block :type))] 100 (if (. block :is-metadata) 101 (set metadata (rule-set block metadata)) ; Handle metadata, TODO this doesn't handle sections for metadata semantics 102 (set rendered (.. rendered (rule-set block)))))) 103 ;(_G.pp rendered))) ; Write to the rendered string 104 (set rendered (.. rendered "\n<break/>\n"))) ; TODO Section "rendering" 105 ; Should each section be returned seperately? 106 ; Then we can add metadata to it easily. 107 ; Final section 108 (values rendered metadata)) 109 110 {: render 111 : render-grammar}