/ 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}