summaryrefslogtreecommitdiff
path: root/src/clarktown/renderers
diff options
context:
space:
mode:
authorAsko Nõmm <84135165+askonomm@users.noreply.github.com>2022-04-23 02:43:49 +0300
committerGitHub <noreply@github.com>2022-04-23 02:43:49 +0300
commit70d88384ea788f7b2ad5ebb725762f7d27300504 (patch)
treeb371f12617949daf2b40538ba9a2e4457b657d0f /src/clarktown/renderers
parent059bfa7bd9bfdde0c75646bf1dfc20d23da8a02c (diff)
parentdc921cbe1c786995a8670efd9f5556ee9df064f9 (diff)
Merge pull request #17 from askonomm/16-improve-architecture
Improve architecture
Diffstat (limited to 'src/clarktown/renderers')
-rw-r--r--src/clarktown/renderers/bold.clj18
-rw-r--r--src/clarktown/renderers/code_block.clj23
-rw-r--r--src/clarktown/renderers/empty_block.clj7
-rw-r--r--src/clarktown/renderers/heading_block.clj44
-rw-r--r--src/clarktown/renderers/horizontal_line_block.clj7
-rw-r--r--src/clarktown/renderers/inline_code.clj21
-rw-r--r--src/clarktown/renderers/italic.clj18
-rw-r--r--src/clarktown/renderers/link_and_image.clj27
-rw-r--r--src/clarktown/renderers/list_block.clj120
-rw-r--r--src/clarktown/renderers/paragraph_block.clj9
-rw-r--r--src/clarktown/renderers/quote_block.clj16
-rw-r--r--src/clarktown/renderers/strikethrough.clj18
12 files changed, 328 insertions, 0 deletions
diff --git a/src/clarktown/renderers/bold.clj b/src/clarktown/renderers/bold.clj
new file mode 100644
index 0000000..64d6137
--- /dev/null
+++ b/src/clarktown/renderers/bold.clj
@@ -0,0 +1,18 @@
+(ns clarktown.renderers.bold
+ (:require
+ [clojure.string :as string]))
+
+
+(defn render
+ "Renders all occurring bold text as bold."
+ [block _ _]
+ (loop [block block
+ matches (-> (re-seq #"(\*{2}|\_{2})[^\*|\_](.*?)[^\*|\_](\*{2}|\_{2})" block)
+ distinct)]
+ (if (empty? matches)
+ block
+ (let [match (ffirst matches)
+ value (subs match 2 (- (count match) 2))
+ replacement (str "<strong>" value "</strong>")]
+ (recur (string/replace block match replacement)
+ (drop 1 matches))))))
diff --git a/src/clarktown/renderers/code_block.clj b/src/clarktown/renderers/code_block.clj
new file mode 100644
index 0000000..184e90e
--- /dev/null
+++ b/src/clarktown/renderers/code_block.clj
@@ -0,0 +1,23 @@
+(ns clarktown.renderers.code-block
+ (:require
+ [clojure.string :as string]))
+
+
+(defn render
+ "Renders the code block."
+ [block _ _]
+ (let [language (->> block
+ (re-find #"\`\`\`(\w+)")
+ second)
+ lines (string/split-lines block)
+ block* (->> (next lines)
+ (take (- (count lines) 2))
+ (string/join \newline))
+ code (-> block*
+ (string/replace #"&" "&amp;")
+ (string/replace #"<" "&lt;")
+ (string/replace #">" "&gt;")
+ string/trim)]
+ (if language
+ (str "<pre><code class=\"language-" language "\">" code "</code></pre>")
+ (str "<pre><code>" code "</code></pre>"))))
diff --git a/src/clarktown/renderers/empty_block.clj b/src/clarktown/renderers/empty_block.clj
new file mode 100644
index 0000000..84df1fb
--- /dev/null
+++ b/src/clarktown/renderers/empty_block.clj
@@ -0,0 +1,7 @@
+(ns clarktown.renderers.empty-block)
+
+
+(defn render
+ "Renders an empty block."
+ [_ _ _]
+ "")
diff --git a/src/clarktown/renderers/heading_block.clj b/src/clarktown/renderers/heading_block.clj
new file mode 100644
index 0000000..4da9bda
--- /dev/null
+++ b/src/clarktown/renderers/heading_block.clj
@@ -0,0 +1,44 @@
+(ns clarktown.renderers.heading-block
+ (:require
+ [clojure.string :as string]
+ [clarktown.matchers.heading-block :as matcher]))
+
+
+(defn render-atx-heading
+ "Renders the hashbang heading block."
+ [block]
+ (let [single-line-block (-> (string/replace block #"\n" "")
+ string/trim)
+ size (-> (string/split single-line-block #" ")
+ first
+ string/trim
+ count)
+ value (->> (string/split single-line-block #" ")
+ next
+ (string/join " ")
+ string/trim)]
+ (str "<h" size ">" value "</h" size ">")))
+
+
+(defn render-settext-heading
+ "Renders the settext heading block."
+ [block]
+ (let [lines (string/split-lines block)
+ value (->> (split-at (- (count lines) 1) lines)
+ first
+ (string/join "\n"))
+ h1? (= "=" (-> (last lines)
+ string/trim
+ (string/split #"")
+ first))]
+ (if h1?
+ (str "<h1>" value "</h1>")
+ (str "<h2>" value "</h2>"))))
+
+
+(defn render
+ "Renders the heading block."
+ [block _ _]
+ (if (matcher/is-atx-heading? block)
+ (render-atx-heading block)
+ (render-settext-heading block)))
diff --git a/src/clarktown/renderers/horizontal_line_block.clj b/src/clarktown/renderers/horizontal_line_block.clj
new file mode 100644
index 0000000..14e5d8a
--- /dev/null
+++ b/src/clarktown/renderers/horizontal_line_block.clj
@@ -0,0 +1,7 @@
+(ns clarktown.renderers.horizontal-line-block)
+
+
+(defn render
+ "Renders the horizontal line block."
+ [_ _ _]
+ "<hr>")
diff --git a/src/clarktown/renderers/inline_code.clj b/src/clarktown/renderers/inline_code.clj
new file mode 100644
index 0000000..e8c298f
--- /dev/null
+++ b/src/clarktown/renderers/inline_code.clj
@@ -0,0 +1,21 @@
+(ns clarktown.renderers.inline-code
+ (:require
+ [clojure.string :as string]))
+
+
+(defn render
+ "Renders all occurring inline code."
+ [block _ _]
+ (loop [block block
+ matches (-> (re-seq #"\`.*?\`" block)
+ distinct)]
+ (if (empty? matches)
+ block
+ (let [match (first matches)
+ value (-> (subs match 1 (- (count match) 1))
+ (string/replace #"&" "&amp;")
+ (string/replace #"<" "&lt;")
+ (string/replace #">" "&gt;"))
+ replacement (str "<code>" value "</code>")]
+ (recur (string/replace block match replacement)
+ (drop 1 matches))))))
diff --git a/src/clarktown/renderers/italic.clj b/src/clarktown/renderers/italic.clj
new file mode 100644
index 0000000..970364e
--- /dev/null
+++ b/src/clarktown/renderers/italic.clj
@@ -0,0 +1,18 @@
+(ns clarktown.renderers.italic
+ (:require
+ [clojure.string :as string]))
+
+
+(defn render
+ "Renders all occurring italic text as italic."
+ [block _ _]
+ (loop [block block
+ matches (-> (re-seq #"(\*{1,}?|\_{1,}?)(.*?)(\*{1,}?|\_{1,}?)" block)
+ distinct)]
+ (if (empty? matches)
+ block
+ (let [match (ffirst matches)
+ value (subs match 1 (- (count match) 1))
+ replacement (str "<em>" value "</em>")]
+ (recur (string/replace block match replacement)
+ (drop 1 matches))))))
diff --git a/src/clarktown/renderers/link_and_image.clj b/src/clarktown/renderers/link_and_image.clj
new file mode 100644
index 0000000..e61503e
--- /dev/null
+++ b/src/clarktown/renderers/link_and_image.clj
@@ -0,0 +1,27 @@
+(ns clarktown.renderers.link-and-image
+ (:require
+ [clojure.string :as string]))
+
+
+(defn encode-href
+ [href]
+ (-> href
+ (string/replace "_" "&#95;")))
+
+
+(defn render
+ "Renders all occurring links and images."
+ [block _ _]
+ (loop [block block
+ matches (-> (re-seq #"\!?\[([a-zA-Z0-9\-\.\,]+( [a-zA-Z0-9\-\.\,]+)*)\]\((.*?)\)" block)
+ distinct)]
+ (if (empty? matches)
+ block
+ (let [[whole-match label _ href] (first matches)
+ image? (string/starts-with? whole-match "!")
+ image (str "<img src=\"" (encode-href href) "\" alt=\"" label "\">")
+ link (str "<a href=\"" (encode-href href) "\">" label "</a>")]
+ (recur (if image?
+ (string/replace block whole-match image)
+ (string/replace block whole-match link))
+ (drop 1 matches)))))) \ No newline at end of file
diff --git a/src/clarktown/renderers/list_block.clj b/src/clarktown/renderers/list_block.clj
new file mode 100644
index 0000000..0c07aaa
--- /dev/null
+++ b/src/clarktown/renderers/list_block.clj
@@ -0,0 +1,120 @@
+(ns clarktown.renderers.list-block
+ (:require
+ [clojure.string :as string]))
+
+
+(defn string->indent-n
+ "Returns the indentation count from left of `str`, which must be
+ in spaces and not tabs."
+ [str]
+ (count (take-while #{\space} str)))
+
+
+(defn compose-items-with-indent-guides
+ "Composes a vector of maps from given `block` that adds a unique
+ ID to each line as well as its `indent-n` which is used later
+ on to determine hierarchies. "
+ [block]
+ (->> (string/split-lines block)
+ (mapv
+ (fn [line]
+ {:id (random-uuid)
+ :indent-n (string->indent-n line)
+ :value (string/trim line)}))))
+
+
+(defn find-parent-id
+ "Assuming a 1-level `items`, will attempt to find the parent `id`
+ of the item at given `index`. Will return `nil` otherwise."
+ [items index]
+ (let [indent-n-at-index (:indent-n (nth items index))]
+ (-> (->> (split-at index items)
+ first
+ reverse
+ (remove #(or (> (:indent-n %) indent-n-at-index)
+ (= (:indent-n %) indent-n-at-index)))
+ first
+ :id))))
+
+
+(defn compose-items-with-parents
+ "Composes a 1-level list of items from `block` and adds parent
+ information to each if they belong to another item. The result
+ of this is used to build the final data tree."
+ [block]
+ (let [items (compose-items-with-indent-guides block)]
+ (->> items
+ (map-indexed
+ (fn [index line]
+ (merge line {:parent (find-parent-id items index)}))))))
+
+
+(defn add-to-parent
+ "Recursively scans `items`, which can be multiple levels deep,
+ and tries to find a home for `item` according to its parent ID."
+ [items item]
+ (->> items
+ (mapv
+ (fn [i]
+ (let [new-item (select-keys item [:id :value])]
+ (if (= (:id i) (:parent item))
+ (if (:items i)
+ (assoc i :items (concat (:items i) [new-item]))
+ (assoc i :items [new-item]))
+ (if (:items i)
+ (assoc i :items (add-to-parent (:items i) item))
+ i)))))))
+
+
+(defn compose-item-tree
+ "Given a `block`, composes a data representation of it based on
+ the indentation of each line."
+ [block]
+ (loop [result []
+ items (compose-items-with-parents block)]
+ (if (empty? items)
+ result
+ (let [item (first items)
+ parent (:parent item)
+ new-item (select-keys item [:id :value])]
+ (recur (if parent
+ (add-to-parent result item)
+ (concat result [new-item]))
+ (drop 1 items))))))
+
+
+(defn render-items
+ "Renders an ordered/un-ordered list hierarchy from given `items`."
+ [items]
+ (loop [result ""
+ inner-items items]
+ (if (empty? inner-items)
+ (if (or (string/starts-with? (:value (first items)) "*")
+ (string/starts-with? (:value (first items)) "-"))
+ (str "<ul>" result "</ul>")
+ (str "<ol>" result "</ol>"))
+ (let [inner-item (first inner-items)
+ value (cond
+ ; * unordered list
+ (string/starts-with? (:value inner-item) "*")
+ (-> (string/replace-first (:value inner-item) "*" "")
+ string/trim)
+ ; - unordered list
+ (string/starts-with? (:value inner-item) "-")
+ (-> (string/replace-first (:value inner-item) "-" "")
+ string/trim)
+ ; ordered list
+ :else
+ (-> (string/replace-first (:value inner-item) #"\d\." "")
+ string/trim))]
+ (recur (if (:items inner-item)
+ (str result "<li>" value (render-items (:items inner-item)) "</li>")
+ (str result "<li>" value "</li>"))
+ (drop 1 inner-items))))))
+
+
+(defn render
+ "Renders the list block"
+ [block _ _]
+ (-> (compose-item-tree block)
+ (render-items)))
diff --git a/src/clarktown/renderers/paragraph_block.clj b/src/clarktown/renderers/paragraph_block.clj
new file mode 100644
index 0000000..0ab3788
--- /dev/null
+++ b/src/clarktown/renderers/paragraph_block.clj
@@ -0,0 +1,9 @@
+(ns clarktown.renderers.paragraph-block
+ (:require
+ [clojure.string :as string]))
+
+
+(defn render
+ "Renders the paragraph block."
+ [block _ _]
+ (str "<p>" (string/trim block) "</p>"))
diff --git a/src/clarktown/renderers/quote_block.clj b/src/clarktown/renderers/quote_block.clj
new file mode 100644
index 0000000..1a302f9
--- /dev/null
+++ b/src/clarktown/renderers/quote_block.clj
@@ -0,0 +1,16 @@
+(ns clarktown.renderers.quote-block
+ (:require
+ [clojure.string :as string]
+ [clarktown.engine :as engine]))
+
+
+(defn render
+ "Renders a quote block."
+ [block parsers correctors]
+ (let [matches (re-seq #">.*" block)
+ blocks (->> (for [match matches]
+ (-> (subs match 1)
+ string/trim))
+ (string/join "\n"))
+ block (engine/render blocks parsers correctors)]
+ (str "<blockquote>" block "</blockquote>")))
diff --git a/src/clarktown/renderers/strikethrough.clj b/src/clarktown/renderers/strikethrough.clj
new file mode 100644
index 0000000..133be47
--- /dev/null
+++ b/src/clarktown/renderers/strikethrough.clj
@@ -0,0 +1,18 @@
+(ns clarktown.renderers.strikethrough
+ (:require
+ [clojure.string :as string]))
+
+
+(defn render
+ "Renders all occurring strikethrough text."
+ [block _ _]
+ (loop [block block
+ matches (-> (re-seq #"~~.*?~~" block)
+ distinct)]
+ (if (empty? matches)
+ block
+ (let [match (first matches)
+ value (subs match 2 (- (count match) 2))
+ replacement (str "<del>" value "</del>")]
+ (recur (string/replace block match replacement)
+ (drop 1 matches))))))