diff options
| author | Asko Nõmm <84135165+askonomm@users.noreply.github.com> | 2022-04-23 02:43:49 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-04-23 02:43:49 +0300 |
| commit | 70d88384ea788f7b2ad5ebb725762f7d27300504 (patch) | |
| tree | b371f12617949daf2b40538ba9a2e4457b657d0f /src/clarktown/renderers | |
| parent | 059bfa7bd9bfdde0c75646bf1dfc20d23da8a02c (diff) | |
| parent | dc921cbe1c786995a8670efd9f5556ee9df064f9 (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.clj | 18 | ||||
| -rw-r--r-- | src/clarktown/renderers/code_block.clj | 23 | ||||
| -rw-r--r-- | src/clarktown/renderers/empty_block.clj | 7 | ||||
| -rw-r--r-- | src/clarktown/renderers/heading_block.clj | 44 | ||||
| -rw-r--r-- | src/clarktown/renderers/horizontal_line_block.clj | 7 | ||||
| -rw-r--r-- | src/clarktown/renderers/inline_code.clj | 21 | ||||
| -rw-r--r-- | src/clarktown/renderers/italic.clj | 18 | ||||
| -rw-r--r-- | src/clarktown/renderers/link_and_image.clj | 27 | ||||
| -rw-r--r-- | src/clarktown/renderers/list_block.clj | 120 | ||||
| -rw-r--r-- | src/clarktown/renderers/paragraph_block.clj | 9 | ||||
| -rw-r--r-- | src/clarktown/renderers/quote_block.clj | 16 | ||||
| -rw-r--r-- | src/clarktown/renderers/strikethrough.clj | 18 |
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 #"&" "&") + (string/replace #"<" "<") + (string/replace #">" ">") + 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 #"&" "&") + (string/replace #"<" "<") + (string/replace #">" ">")) + 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 "_" "_"))) + + +(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)))))) |
