diff options
| author | Asko Nõmm <asko@bien.ee> | 2022-04-19 17:50:19 +0300 |
|---|---|---|
| committer | Asko Nõmm <asko@bien.ee> | 2022-04-19 17:50:19 +0300 |
| commit | 52203a49aa544b2c11c96445d8732893160c436b (patch) | |
| tree | 84d7b0e9af92770647643bdf7ddbb898663bd7b8 | |
| parent | 059bfa7bd9bfdde0c75646bf1dfc20d23da8a02c (diff) | |
WIP #16
Pretty much done. Needs more testing. And new documentation.
42 files changed, 495 insertions, 410 deletions
@@ -35,7 +35,7 @@ At its core, Clarktown is nothing more than a collection of parsers that collect - `strikethrough` So whenever you call `clarktown.core/render`, those parsers are applied to the content you give it, and are defined collectively in -`clarktown.parsers/parsers`. If you want to remove certain parsers feel free to duplicate that vector with whatever combination of +`clarktown.renderers/parsers`. If you want to remove certain parsers feel free to duplicate that vector with whatever combination of parsers that works best for you, and pass it as the second parameter to the `clarktown.core/render` function, like so: ```clojure @@ -45,7 +45,7 @@ parsers that works best for you, and pass it as the second parameter to the `cla ## Create your own parsers As Clarktown is modular, you can easily create your own custom parsers as well. To see how the parsers are made, I really recommend -checking any of the existing parsers you can find in the `clarktown.parsers/*` namespaces and how they are used in the `clarktown.parsers/parsers` variable, +checking any of the existing parsers you can find in the `clarktown.renderers/*` namespaces and how they are used in the `clarktown.renderers/parsers` variable, but overall the idea is very simple: there are (potential) matchers, and renderers. However, before we get into matchers and renderers, let me quickly explain how Clarktown does its thing. Clarktown splits the entire Markdown @@ -112,7 +112,7 @@ vector of maps, each map representing one collection. And so to register you cou (ns myapp.core (:require [clarktown.core :as clarktown] - [clarktown.parsers :refer [parsers]])) + [clarktown.renderers :refer [parsers]])) (def my-parser {:matcher heading-block/is? diff --git a/src/clarktown/core.clj b/src/clarktown/core.clj index a6ed42c..cabd6e6 100644 --- a/src/clarktown/core.clj +++ b/src/clarktown/core.clj @@ -1,7 +1,145 @@ (ns clarktown.core (:require - [clarktown.parser :as parser] - [clarktown.parsers :as parsers])) + [clojure.string :as string] + [clarktown.parsers :as parsers] + [clarktown.correctors :as correctors])) + + +(defn- stitch-code-blocks + "Since code blocks can span multiple blocks (a block is separated by + two line breaks from another block) , we need to stitch them together + into one block in order for a block parser to be able to do anything + with it." + [blocks] + (loop [stitched-blocks [] + code-block-started? false + blocks blocks] + (if (empty? blocks) + stitched-blocks + (let [block (first blocks)] + (if (and (string/starts-with? (string/trim block) "```") + (not (string/ends-with? (string/trim block) "```"))) + (recur (conj stitched-blocks block) + true + (drop 1 blocks)) + (if code-block-started? + (let [last-block (last stitched-blocks) + last-block-index (- (count stitched-blocks) 1)] + (if (string/ends-with? (string/trim block) "```") + (recur (assoc stitched-blocks last-block-index (str last-block "\n\n" block)) + false + (drop 1 blocks)) + (recur (assoc stitched-blocks last-block-index (str last-block "\n\n" block)) + true + (drop 1 blocks)))) + (recur (conj stitched-blocks block) + false + (drop 1 blocks)))))))) + + +(defn- correct-block-separations + "Corrects block separations and adds newlines above or + below a block where needed." + [correctors lines] + (->> lines + (map-indexed + (fn [index line] + (let [add-line-above? (some #(true? (% lines line index)) (:empty-line-above? correctors)) + add-line-below? (some #(true? (% lines line index)) (:empty-line-below? correctors))] + (cond + ; If code block starts but there is no empty newline + ; above, let's fix that + (and add-line-above? + (not add-line-below?)) + (str \newline line) + + ; If the code block ends, but there is no empty newline + ; below, let's fix that. + (and add-line-below? + (not add-line-above?)) + (str line \newline) + + ; If the code block needs a newline both above and below, + ; let's fix that. + (and add-line-above? + add-line-below?) + (str \newline line \newline) + + ; otherwise is what it is + :else line)))))) + + +(defn- correct-markdown + "Corrects invalid Markdown for the parser." + [markdown given-correctors] + (let [lines (string/split-lines markdown)] + (->> lines + (correct-block-separations (:block-separations given-correctors)) + (string/join \newline)))) + + +(defn- find-parser-by-block + "Find a parser from `parsers` that matches the given `block`." + [parsers block] + (->> parsers + (filter + (fn [{:keys [matcher]}] + (when matcher + (matcher block)))) + first)) + + +(defn- parse-block-with-known-parser + "Parses a given `block` with a known `parser`." + [parser parsers block] + (loop [block block + renderers (:renderers parser)] + (if (empty? renderers) + block + (let [renderer (first renderers)] + (recur (renderer block parsers) + (drop 1 renderers)))))) + + +(defn- parse-block-with-unknown-parsers + "Parses the given `block` with all the parsers that do not have + a matcher function, useful for any fallback parsing one might want + to do." + [parsers block] + (loop [block block + parsers (filter #(= nil (:matcher %)) parsers)] + (if (empty? parsers) + block + (recur (loop [block block + renderers (:renderers (first parsers))] + (if (empty? renderers) + block + (let [renderer (first renderers)] + (recur (renderer block parsers) + (drop 1 renderers))))) + (drop 1 parsers))))) + + +(defn- parse-blocks + "Parses each individual Markdown block, given as `blocks`, with + the list of `parsers`." + [blocks parsers] + (for [block blocks] + (if-let [parser (find-parser-by-block parsers block)] + (->> (string/trim block) + (parse-block-with-known-parser parser parsers)) + (->> (string/trim block) + (parse-block-with-unknown-parsers parsers))))) + + +(defn parse + "Parses given `markdown` with `parsers`." + [markdown given-parsers given-correctors] + (let [blocks (-> (correct-markdown markdown given-correctors) + (string/split #"\n\n") + stitch-code-blocks) + parsed-blocks (parse-blocks blocks given-parsers)] + (string/join "\n\n" parsed-blocks))) (defn render @@ -22,9 +160,8 @@ :renderers [(fn [block] ...) (fn [block] ...)]} ```" ([markdown] - (render markdown parsers/parsers)) + (render markdown parsers/default-parsers)) ([markdown given-parsers] - (parser/parse markdown given-parsers))) - -(comment - (render (slurp "./test.md")))
\ No newline at end of file + (render markdown given-parsers correctors/default-correctors)) + ([markdown given-parsers given-correctors] + (parse markdown given-parsers given-correctors)))
\ No newline at end of file diff --git a/src/clarktown/correctors.clj b/src/clarktown/correctors.clj new file mode 100644 index 0000000..ba49879 --- /dev/null +++ b/src/clarktown/correctors.clj @@ -0,0 +1,17 @@ +(ns clarktown.correctors + (:require + [clarktown.correctors.code-block :as code-block] + [clarktown.correctors.atx-heading-block :as atx-heading-block])) + + +(def block-separation-correctors + {:empty-line-above? + [code-block/empty-line-above? + atx-heading-block/empty-line-above?] + :empty-line-below? + [code-block/empty-line-below? + atx-heading-block/empty-line-below?]}) + + +(def default-correctors + {:block-separations block-separation-correctors})
\ No newline at end of file diff --git a/src/clarktown/correctors/atx_heading_block.clj b/src/clarktown/correctors/atx_heading_block.clj new file mode 100644 index 0000000..a792572 --- /dev/null +++ b/src/clarktown/correctors/atx_heading_block.clj @@ -0,0 +1,19 @@ +(ns clarktown.correctors.atx-heading-block + (:require + [clojure.string :as string])) + + +(defn empty-line-above? + [lines line index] + (and (string/starts-with? (string/trim line) "#") + (> index 0) + (not (= (-> (nth lines (- index 1)) + string/trim) "")))) + + +(defn empty-line-below? + [lines line index] + (and (string/starts-with? (string/trim line) "#") + (< index (- (count lines) 1)) + (not (= (-> (nth lines (+ index 1)) + string/trim) ""))))
\ No newline at end of file diff --git a/src/clarktown/correctors/code_block.clj b/src/clarktown/correctors/code_block.clj new file mode 100644 index 0000000..e767390 --- /dev/null +++ b/src/clarktown/correctors/code_block.clj @@ -0,0 +1,27 @@ +(ns clarktown.correctors.code-block + (:require + [clojure.string :as string])) + + +(defn empty-line-above? + [lines line index] + (and (= (string/trim line) "```") + (> index 0) + (->> (take index lines) + (filter #(= (string/trim %) "```")) + count + odd?) + (not (= (-> (nth lines (- index 1)) + string/trim) "")))) + + +(defn empty-line-below? + [lines line index] + (and (= (string/trim line) "```") + (< index (- (count lines) 1)) + (->> (take index lines) + (filter #(= (string/trim %) "```")) + count + even?) + (not (= (-> (nth lines (+ index 1)) + string/trim) ""))))
\ No newline at end of file diff --git a/src/clarktown/matchers/code_block.clj b/src/clarktown/matchers/code_block.clj new file mode 100644 index 0000000..655c951 --- /dev/null +++ b/src/clarktown/matchers/code_block.clj @@ -0,0 +1,10 @@ +(ns clarktown.matchers.code-block + (:require + [clojure.string :as string])) + + +(defn match? + "Determines whether we're dealing with a code block." + [block] + (and (string/starts-with? block "```") + (string/ends-with? block "```")))
\ No newline at end of file diff --git a/src/clarktown/parsers/empty_block.clj b/src/clarktown/matchers/empty_block.clj index 0ed5a08..cc7b7f4 100644 --- a/src/clarktown/parsers/empty_block.clj +++ b/src/clarktown/matchers/empty_block.clj @@ -1,17 +1,11 @@ -(ns clarktown.parsers.empty-block +(ns clarktown.matchers.empty-block (:require [clojure.string :as string])) -(defn is? +(defn match? "Determines if the current block is an empty block or not." [block] (-> (string/replace block #"\n" "") string/trim - string/blank?)) - - -(defn render - "Renders an empty block." - [_ _] - "") + string/blank?))
\ No newline at end of file diff --git a/src/clarktown/matchers/heading_block.clj b/src/clarktown/matchers/heading_block.clj new file mode 100644 index 0000000..2295f26 --- /dev/null +++ b/src/clarktown/matchers/heading_block.clj @@ -0,0 +1,29 @@ +(ns clarktown.matchers.heading-block + (:require + [clojure.string :as string])) + + +(defn is-atx-heading? + "Determines whether the given block is a atx heading." + [block] + (-> (string/replace block #"\n" "") + string/trim + (string/starts-with? "#"))) + + +(defn is-settext-heading? + "Determines whether the given block is a settext heading." + [block] + (let [lines (-> (string/split-lines block)) + chars (-> (last lines) + string/trim + (string/split #""))] + (and (> (count lines) 1) + (every? #{"-" "="} chars)))) + + +(defn match? + "Determines whether the given block is a heading block." + [block] + (or (is-atx-heading? block) + (is-settext-heading? block)))
\ No newline at end of file diff --git a/src/clarktown/matchers/horizontal_line_block.clj b/src/clarktown/matchers/horizontal_line_block.clj new file mode 100644 index 0000000..a2dde6e --- /dev/null +++ b/src/clarktown/matchers/horizontal_line_block.clj @@ -0,0 +1,10 @@ +(ns clarktown.matchers.horizontal-line-block + (:require + [clojure.string :as string])) + + +(defn match? + "Determines whether the given block is a horizontal line block." + [block] + (or (= "***" (string/trim block)) + (= "---" (string/trim block))))
\ No newline at end of file diff --git a/src/clarktown/matchers/list_block.clj b/src/clarktown/matchers/list_block.clj new file mode 100644 index 0000000..a37b06e --- /dev/null +++ b/src/clarktown/matchers/list_block.clj @@ -0,0 +1,10 @@ +(ns clarktown.matchers.list-block + (:require + [clojure.string :as string])) + + +(defn match? + "Determines whether we're dealing with a list block or not." + [block] + (->> (string/trim block) + (re-matches #"(?s)^(\d\.\s|\*{1}\s|\-{1}\s).*$")))
\ No newline at end of file diff --git a/src/clarktown/matchers/quote_block.clj b/src/clarktown/matchers/quote_block.clj new file mode 100644 index 0000000..230b561 --- /dev/null +++ b/src/clarktown/matchers/quote_block.clj @@ -0,0 +1,11 @@ +(ns clarktown.matchers.quote-block + (:require + [clojure.string :as string])) + + +(defn match? + "Determines whether the given block is a quote block." + [block] + (-> (string/replace block #"\n" "") + string/trim + (string/starts-with? ">")))
\ No newline at end of file diff --git a/src/clarktown/parser.clj b/src/clarktown/parser.clj deleted file mode 100644 index ecee37d..0000000 --- a/src/clarktown/parser.clj +++ /dev/null @@ -1,195 +0,0 @@ -(ns clarktown.parser - (:require - [clojure.string :as string])) - - -(defn- stitch-code-blocks - "Since code blocks can span multiple blocks (a block is separated by - two line breaks from another block) , we need to stitch them together - into one block in order for a block parser to be able to do anything - with it." - [blocks] - (loop [stitched-blocks [] - code-block-started? false - blocks blocks] - (if (empty? blocks) - stitched-blocks - (let [block (first blocks)] - (if (and (string/starts-with? (string/trim block) "```") - (not (string/ends-with? (string/trim block) "```"))) - (recur (conj stitched-blocks block) - true - (drop 1 blocks)) - (if code-block-started? - (let [last-block (last stitched-blocks) - last-block-index (- (count stitched-blocks) 1)] - (if (string/ends-with? (string/trim block) "```") - (recur (assoc stitched-blocks last-block-index (str last-block "\n\n" block)) - false - (drop 1 blocks)) - (recur (assoc stitched-blocks last-block-index (str last-block "\n\n" block)) - true - (drop 1 blocks)))) - (recur (conj stitched-blocks block) - false - (drop 1 blocks)))))))) - - -(defn- needs-empty-line-above? - "Determines whether the current line needs an empty line correction - above." - [lines line index] - (cond - ; code block - (and (= (string/trim line) "```") - (> index 0) - (->> (take index lines) - (filter #(= (string/trim %) "```")) - count - odd?) - (not (= (-> (nth lines (- index 1)) - string/trim) ""))) - true - - ; ATX heading block - (and (string/starts-with? (string/trim line) "#") - (> index 0) - (not (= (-> (nth lines (- index 1)) - string/trim) ""))) - true - - - ; everything else stays normal - :else false)) - - -(defn- needs-empty-line-below? - "Determines whether the current line needs an empty line correction - below." - [lines line index] - (cond - ; code block - (and (= (string/trim line) "```") - (< index (- (count lines) 1)) - (->> (take index lines) - (filter #(= (string/trim %) "```")) - count - even?) - (not (= (-> (nth lines (+ index 1)) - string/trim) ""))) - true - - ; ATX heading block - (and (string/starts-with? (string/trim line) "#") - (< index (- (count lines) 1)) - (not (= (-> (nth lines (+ index 1)) - string/trim) ""))) - true - - ; everything else stays normal - :else false)) - - -(defn- correct-block-separations - "Corrects block separations and adds newlines above or - below a block where needed." - [lines] - (->> lines - (map-indexed - (fn [index line] - (let [add-line-above? (needs-empty-line-above? lines line index) - add-line-below? (needs-empty-line-below? lines line index)] - (cond - ; If code block starts but there is no empty newline - ; above, let's fix that - (and add-line-above? - (not add-line-below?)) - (str \newline line) - - ; If the code block ends, but there is no empty newline - ; below, let's fix that. - (and add-line-below? - (not add-line-above?)) - (str line \newline) - - ; If the code block needs a newline both above and below, - ; let's fix that. - (and add-line-above? - add-line-below?) - (str \newline line \newline) - - ; otherwise is what it is - :else line)))))) - - -(defn- correct-markdown - "Corrects invalid Markdown for the parser." - [markdown] - (let [lines (string/split-lines markdown)] - (->> lines - correct-block-separations - (string/join \newline)))) - - -(defn- find-parser-by-block - "Find a parser from `parsers` that matches the given `block`." - [parsers block] - (->> parsers - (filter - (fn [{:keys [matcher]}] - (when matcher - (matcher block)))) - first)) - - -(defn- parse-block-with-known-parser - "Parses a given `block` with a known `parser`." - [parser parsers block] - (loop [block block - renderers (:renderers parser)] - (if (empty? renderers) - block - (let [renderer (first renderers)] - (recur (renderer block parsers) - (drop 1 renderers)))))) - - -(defn- parse-block-with-unknown-parsers - "Parses the given `block` with all the parsers that do not have - a matcher function, useful for any fallback parsing one might want - to do." - [parsers block] - (loop [block block - parsers (filter #(= nil (:matcher %)) parsers)] - (if (empty? parsers) - block - (recur (loop [block block - renderers (:renderers (first parsers))] - (if (empty? renderers) - block - (let [renderer (first renderers)] - (recur (renderer block parsers) - (drop 1 renderers))))) - (drop 1 parsers))))) - - -(defn- parse-blocks - "Parses each individual Markdown block, given as `blocks`, with - the list of `parsers`." - [blocks parsers] - (for [block blocks] - (if-let [parser (find-parser-by-block parsers block)] - (->> (string/trim block) - (parse-block-with-known-parser parser parsers)) - (->> (string/trim block) - (parse-block-with-unknown-parsers parsers))))) - - -(defn parse - "Parses given `markdown` with `parsers`." - [markdown parsers] - (let [blocks (-> (correct-markdown markdown) - (string/split #"\n\n") - stitch-code-blocks) - parsed-blocks (parse-blocks blocks parsers)] - (string/join "\n\n" parsed-blocks))) diff --git a/src/clarktown/parsers.clj b/src/clarktown/parsers.clj index cd909b3..77c9794 100644 --- a/src/clarktown/parsers.clj +++ b/src/clarktown/parsers.clj @@ -1,45 +1,95 @@ (ns clarktown.parsers (:require - [clarktown.parsers.bold :as bold] - [clarktown.parsers.italic :as italic] - [clarktown.parsers.inline-code :as inline-code] - [clarktown.parsers.strikethrough :as strikethrough] - [clarktown.parsers.link-and-image :as link-and-image] - [clarktown.parsers.empty-block :as empty-block] - [clarktown.parsers.horizontal-line-block :as horizontal-line-block] - [clarktown.parsers.quote-block :as quote-block] - [clarktown.parsers.heading-block :as heading-block] - [clarktown.parsers.code-block :as code-block] - [clarktown.parsers.list-block :as list-block] - [clarktown.parsers.paragraph-block :as paragraph-block])) - - -(def parsers - [{:matcher empty-block/is? - :renderers [empty-block/render]} - {:matcher horizontal-line-block/is? - :renderers [horizontal-line-block/render]} - {:matcher heading-block/is? - :renderers [link-and-image/render - bold/render - italic/render - inline-code/render - strikethrough/render - heading-block/render]} - {:matcher quote-block/is? - :renderers [quote-block/render]} - {:matcher code-block/is? - :renderers [code-block/render]} - {:matcher list-block/is? - :renderers [link-and-image/render - bold/render - italic/render - inline-code/render - strikethrough/render - list-block/render]} - {:renderers [link-and-image/render - bold/render - italic/render - inline-code/render - strikethrough/render - paragraph-block/render]}]) + [clarktown.matchers.empty-block] + [clarktown.renderers.empty-block] + [clarktown.matchers.horizontal-line-block] + [clarktown.renderers.horizontal-line-block] + [clarktown.matchers.heading-block] + [clarktown.renderers.heading-block] + [clarktown.matchers.quote-block] + [clarktown.renderers.quote-block] + [clarktown.matchers.code-block] + [clarktown.renderers.code-block] + [clarktown.matchers.list-block] + [clarktown.renderers.list-block] + [clarktown.renderers.paragraph-block] + [clarktown.renderers.link-and-image] + [clarktown.renderers.bold] + [clarktown.renderers.italic] + [clarktown.renderers.inline-code] + [clarktown.renderers.strikethrough])) + + +(def + ^{:doc "Detects, parses and renders a empty block."} + empty-block-parser + {:matcher clarktown.matchers.empty-block/match? + :renderers [clarktown.renderers.empty-block/render]}) + + +(def + ^{:doc "Detects, parses and renders a horizontal line block."} + horizontal-line-block-parser + {:matcher clarktown.matchers.horizontal-line-block/match? + :renderers [clarktown.renderers.horizontal-line-block/render]}) + + +(def + ^{:doc "Detects, parses and renders a heading block."} + heading-block-parser + {:matcher clarktown.matchers.heading-block/match? + :renderers [clarktown.renderers.link-and-image/render + clarktown.renderers.bold/render + clarktown.renderers.italic/render + clarktown.renderers.inline-code/render + clarktown.renderers.strikethrough/render + clarktown.renderers.heading-block/render]}) + + +(def + ^{:doc "Detects, parses and renders a quote block."} + quote-block-parser + {:matcher clarktown.matchers.quote-block/match? + :renderers [clarktown.renderers.quote-block/render]}) + + +(def + ^{:doc "Detects, parses and renders a code block."} + code-block-parser + {:matcher clarktown.matchers.code-block/match? + :renderers [clarktown.renderers.code-block/render]}) + + +(def + ^{:doc "Detects, parses and renders a list block."} + list-block-parser + {:matcher clarktown.matchers.list-block/match? + :renderers [clarktown.renderers.link-and-image/render + clarktown.renderers.bold/render + clarktown.renderers.italic/render + clarktown.renderers.inline-code/render + clarktown.renderers.strikethrough/render + clarktown.renderers.list-block/render]}) + + +(def + ^{:doc "Parses and renders a quote block."} + paragraph-block-parser + {:renderers [clarktown.renderers.link-and-image/render + clarktown.renderers.bold/render + clarktown.renderers.italic/render + clarktown.renderers.inline-code/render + clarktown.renderers.strikethrough/render + clarktown.renderers.paragraph-block/render]}) + + +(def + ^{:doc "A set of default parsers."} + default-parsers + [empty-block-parser + horizontal-line-block-parser + heading-block-parser + quote-block-parser + code-block-parser + list-block-parser + paragraph-block-parser]) diff --git a/src/clarktown/parsers/horizontal_line_block.clj b/src/clarktown/parsers/horizontal_line_block.clj deleted file mode 100644 index b1d1e05..0000000 --- a/src/clarktown/parsers/horizontal_line_block.clj +++ /dev/null @@ -1,16 +0,0 @@ -(ns clarktown.parsers.horizontal-line-block - (:require - [clojure.string :as string])) - - -(defn is? - "Determines whether the given block is a horizontal line block." - [block] - (or (= "***" (string/trim block)) - (= "---" (string/trim block)))) - - -(defn render - "Renders the horizontal line block." - [_ _] - "<hr>") diff --git a/src/clarktown/parsers/bold.clj b/src/clarktown/renderers/bold.clj index 79579bf..1ce7f84 100644 --- a/src/clarktown/parsers/bold.clj +++ b/src/clarktown/renderers/bold.clj @@ -1,4 +1,4 @@ -(ns clarktown.parsers.bold +(ns clarktown.renderers.bold (:require [clojure.string :as string])) diff --git a/src/clarktown/parsers/code_block.clj b/src/clarktown/renderers/code_block.clj index c6ecfea..2bed4e6 100644 --- a/src/clarktown/parsers/code_block.clj +++ b/src/clarktown/renderers/code_block.clj @@ -1,15 +1,8 @@ -(ns clarktown.parsers.code-block +(ns clarktown.renderers.code-block (:require [clojure.string :as string])) -(defn is? - "Determines whether we're dealing with a code block." - [block] - (and (string/starts-with? block "```") - (string/ends-with? block "```"))) - - (defn render "Renders the code block." [block _] diff --git a/src/clarktown/renderers/empty_block.clj b/src/clarktown/renderers/empty_block.clj new file mode 100644 index 0000000..66e819e --- /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/parsers/heading_block.clj b/src/clarktown/renderers/heading_block.clj index def2394..f953d0a 100644 --- a/src/clarktown/parsers/heading_block.clj +++ b/src/clarktown/renderers/heading_block.clj @@ -1,35 +1,10 @@ -(ns clarktown.parsers.heading-block +(ns clarktown.renderers.heading-block (:require - [clojure.string :as string])) + [clojure.string :as string] + [clarktown.matchers.heading-block :as matcher])) -(defn is-hashbang-heading? - "Determines whether the given block is a hashbang heading." - [block] - (-> (string/replace block #"\n" "") - string/trim - (string/starts-with? "#"))) - - -(defn is-settext-heading? - "Determines whether the given block is a settext heading." - [block] - (let [lines (-> (string/split-lines block)) - chars (-> (last lines) - string/trim - (string/split #""))] - (and (> (count lines) 1) - (every? #{"-" "="} chars)))) - - -(defn is? - "Determines whether the given block is a heading block." - [block] - (or (is-hashbang-heading? block) - (is-settext-heading? block))) - - -(defn render-hashbang-heading +(defn render-atx-heading "Renders the hashbang heading block." [block] (let [single-line-block (-> (string/replace block #"\n" "") @@ -61,12 +36,9 @@ (str "<h2>" value "</h2>")))) -(render-settext-heading "Hello world\nAnd you too\n===") - - (defn render "Renders the heading block." [block _] - (if (is-hashbang-heading? block) - (render-hashbang-heading 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..f141e5f --- /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/parsers/inline_code.clj b/src/clarktown/renderers/inline_code.clj index 1de73bf..29593a8 100644 --- a/src/clarktown/parsers/inline_code.clj +++ b/src/clarktown/renderers/inline_code.clj @@ -1,4 +1,4 @@ -(ns clarktown.parsers.inline-code +(ns clarktown.renderers.inline-code (:require [clojure.string :as string])) diff --git a/src/clarktown/parsers/italic.clj b/src/clarktown/renderers/italic.clj index 915a017..a1568f6 100644 --- a/src/clarktown/parsers/italic.clj +++ b/src/clarktown/renderers/italic.clj @@ -1,4 +1,4 @@ -(ns clarktown.parsers.italic +(ns clarktown.renderers.italic (:require [clojure.string :as string])) diff --git a/src/clarktown/parsers/link_and_image.clj b/src/clarktown/renderers/link_and_image.clj index 3be2efe..ea4a006 100644 --- a/src/clarktown/parsers/link_and_image.clj +++ b/src/clarktown/renderers/link_and_image.clj @@ -1,4 +1,4 @@ -(ns clarktown.parsers.link-and-image +(ns clarktown.renderers.link-and-image (:require [clojure.string :as string])) diff --git a/src/clarktown/parsers/list_block.clj b/src/clarktown/renderers/list_block.clj index 52f955f..2a40b06 100644 --- a/src/clarktown/parsers/list_block.clj +++ b/src/clarktown/renderers/list_block.clj @@ -1,15 +1,8 @@ -(ns clarktown.parsers.list-block +(ns clarktown.renderers.list-block (:require [clojure.string :as string])) -(defn is? - "Determines whether we're dealing with a list block or not." - [block] - (->> (string/trim block) - (re-matches #"(?s)^(\d\.\s|\*{1}\s|\-{1}\s).*$"))) - - (defn string->indent-n "Returns the indentation count from left of `str`, which must be in spaces and not tabs." @@ -111,6 +104,7 @@ (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))] diff --git a/src/clarktown/parsers/paragraph_block.clj b/src/clarktown/renderers/paragraph_block.clj index 3a7c633..c7bec22 100644 --- a/src/clarktown/parsers/paragraph_block.clj +++ b/src/clarktown/renderers/paragraph_block.clj @@ -1,4 +1,4 @@ -(ns clarktown.parsers.paragraph-block +(ns clarktown.renderers.paragraph-block (:require [clojure.string :as string])) diff --git a/src/clarktown/parsers/quote_block.clj b/src/clarktown/renderers/quote_block.clj index ff2dda8..ee30635 100644 --- a/src/clarktown/parsers/quote_block.clj +++ b/src/clarktown/renderers/quote_block.clj @@ -1,17 +1,9 @@ -(ns clarktown.parsers.quote-block +(ns clarktown.renderers.quote-block (:require [clojure.string :as string] [clarktown.parser :as parser])) -(defn is? - "Determines whether the given block is a quote block." - [block] - (-> (string/replace block #"\n" "") - string/trim - (string/starts-with? ">"))) - - (defn render "Renders a quote block." [block parsers] diff --git a/src/clarktown/parsers/strikethrough.clj b/src/clarktown/renderers/strikethrough.clj index 6f03152..8e124a0 100644 --- a/src/clarktown/parsers/strikethrough.clj +++ b/src/clarktown/renderers/strikethrough.clj @@ -1,4 +1,4 @@ -(ns clarktown.parsers.strikethrough +(ns clarktown.renderers.strikethrough (:require [clojure.string :as string])) diff --git a/test/clarktown/matchers/empty_block_test.clj b/test/clarktown/matchers/empty_block_test.clj new file mode 100644 index 0000000..8fe83e4 --- /dev/null +++ b/test/clarktown/matchers/empty_block_test.clj @@ -0,0 +1,9 @@ +(ns clarktown.matchers.empty-block-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.matchers.empty-block :as empty-block])) + +(deftest empty-block-matcher-test + (testing "Checking an empty block" + (is (true? (empty-block/match? ""))) + (is (true? (empty-block/match? " ")))))
\ No newline at end of file diff --git a/test/clarktown/matchers/horizontal_line_block_test.clj b/test/clarktown/matchers/horizontal_line_block_test.clj new file mode 100644 index 0000000..c3402d2 --- /dev/null +++ b/test/clarktown/matchers/horizontal_line_block_test.clj @@ -0,0 +1,14 @@ +(ns clarktown.matchers.horizontal-line-block-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.matchers.horizontal-line-block :as horizontal-line-block])) + + +(deftest horizontal-line-block-matcher-test + (testing "Is a horizontal line block" + (is (true? (horizontal-line-block/match? "***"))) + (is (true? (horizontal-line-block/match? " ***"))) + (is (false? (horizontal-line-block/match? "Test *** 123"))) + (is (true? (horizontal-line-block/match? "---"))) + (is (true? (horizontal-line-block/match? " ---"))) + (is (false? (horizontal-line-block/match? "Test --- 123")))))
\ No newline at end of file diff --git a/test/clarktown/matchers/quote_block_test.clj b/test/clarktown/matchers/quote_block_test.clj new file mode 100644 index 0000000..05288d6 --- /dev/null +++ b/test/clarktown/matchers/quote_block_test.clj @@ -0,0 +1,11 @@ +(ns clarktown.matchers.quote-block-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.matchers.quote-block :as quote-block])) + + +(deftest quote-block-block-matcher-test + (testing "Checking a quote block" + (is (true? (quote-block/match? "> Test"))) + (is (true? (quote-block/match? " > Test"))) + (is (true? (quote-block/match? ">")))))
\ No newline at end of file diff --git a/test/clarktown/parsers/empty_block_test.clj b/test/clarktown/parsers/empty_block_test.clj deleted file mode 100644 index a8d89c4..0000000 --- a/test/clarktown/parsers/empty_block_test.clj +++ /dev/null @@ -1,14 +0,0 @@ -(ns clarktown.parsers.empty-block-test - (:require - [clojure.test :refer [deftest testing is]] - [clarktown.parsers.empty-block :as empty-block])) - - -(deftest empty-block-test - (testing "Rendering an empty block" - (is (= (empty-block/render "" nil) - ""))) - - (testing "Checking an empty block" - (is (true? (empty-block/is? ""))) - (is (true? (empty-block/is? " "))))) diff --git a/test/clarktown/parsers/horizontal_line_block_test.clj b/test/clarktown/parsers/horizontal_line_block_test.clj deleted file mode 100644 index 21617b6..0000000 --- a/test/clarktown/parsers/horizontal_line_block_test.clj +++ /dev/null @@ -1,21 +0,0 @@ -(ns clarktown.parsers.horizontal-line-block-test - (:require - [clojure.test :refer [deftest testing is]] - [clarktown.parsers.horizontal-line-block :as horizontal-line-block])) - - -(deftest horizontal-line-block-test - (testing "Creating a horizontal line" - (is (= "<hr>" - (horizontal-line-block/render "***" nil))) - - (is (= "<hr>" - (horizontal-line-block/render "---" nil)))) - - (testing "Is a horizontal line block" - (is (true? (horizontal-line-block/is? "***"))) - (is (true? (horizontal-line-block/is? " ***"))) - (is (false? (horizontal-line-block/is? "Test *** 123"))) - (is (true? (horizontal-line-block/is? "---"))) - (is (true? (horizontal-line-block/is? " ---"))) - (is (false? (horizontal-line-block/is? "Test --- 123")))))
\ No newline at end of file diff --git a/test/clarktown/parsers/quote_block_test.clj b/test/clarktown/parsers/quote_block_test.clj deleted file mode 100644 index 94553cf..0000000 --- a/test/clarktown/parsers/quote_block_test.clj +++ /dev/null @@ -1,15 +0,0 @@ -(ns clarktown.parsers.quote-block-test - (:require - [clojure.test :refer [deftest testing is]] - [clarktown.parsers.quote-block :as quote-block])) - - -(deftest quote-block-block-test - (testing "Creating a quote block line" - (is (= (quote-block/render "> First line\n> second line" nil) - "<blockquote>First line\nsecond line</blockquote>"))) - - (testing "Checking a quote block" - (is (true? (quote-block/is? "> Test"))) - (is (true? (quote-block/is? " > Test"))) - (is (true? (quote-block/is? ">")))))
\ No newline at end of file diff --git a/test/clarktown/parsers/bold_test.clj b/test/clarktown/renderers/bold_test.clj index a082d41..fba0ea6 100644 --- a/test/clarktown/parsers/bold_test.clj +++ b/test/clarktown/renderers/bold_test.clj @@ -1,10 +1,10 @@ -(ns clarktown.parsers.bold-test +(ns clarktown.renderers.bold-test (:require [clojure.test :refer [deftest testing is]] - [clarktown.parsers.bold :as bold])) + [clarktown.renderers.bold :as bold])) -(deftest bold-test +(deftest bold-renderer-test (testing "Creating bold text with two surrounding asterisk characters" (is (= "<strong>This is bold.</strong>" (bold/render "**This is bold.**" nil)))) diff --git a/test/clarktown/parsers/code_block_test.clj b/test/clarktown/renderers/code_block_test.clj index 8b1113d..37c701b 100644 --- a/test/clarktown/parsers/code_block_test.clj +++ b/test/clarktown/renderers/code_block_test.clj @@ -1,11 +1,11 @@ -(ns clarktown.parsers.code-block-test +(ns clarktown.renderers.code-block-test (:require [clojure.test :refer [deftest testing is]] [clojure.java.io :as io] - [clarktown.parsers.code-block :as code-block])) + [clarktown.renderers.code-block :as code-block])) -(deftest code-block-test +(deftest code-block-renderer-test (testing "Code block with language specification" (is (= (slurp (io/file (io/resource "test/parsers/code_block_result.html"))) (code-block/render (slurp (io/file (io/resource "test/parsers/code_block.md"))) nil)))) diff --git a/test/clarktown/renderers/empty_block_test.clj b/test/clarktown/renderers/empty_block_test.clj new file mode 100644 index 0000000..35fb902 --- /dev/null +++ b/test/clarktown/renderers/empty_block_test.clj @@ -0,0 +1,10 @@ +(ns clarktown.renderers.empty-block-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.renderers.empty-block :as empty-block])) + + +(deftest empty-block-renderer-test + (testing "Rendering an empty block" + (is (= (empty-block/render "" nil) + "")))) diff --git a/test/clarktown/parsers/heading_block_test.clj b/test/clarktown/renderers/heading_block_test.clj index 9bfff4f..9c3386f 100644 --- a/test/clarktown/parsers/heading_block_test.clj +++ b/test/clarktown/renderers/heading_block_test.clj @@ -1,10 +1,10 @@ -(ns clarktown.parsers.heading-block-test +(ns clarktown.renderers.heading-block-test (:require [clojure.test :refer [deftest testing is]] - [clarktown.parsers.heading-block :as heading-block])) + [clarktown.renderers.heading-block :as heading-block])) -(deftest hashbang-heading-test +(deftest atx-heading-renderer-test (testing "Hashbang heading block that's a H1" (is (= "<h1>This is a heading block.</h1>" (heading-block/render "# This is a heading block." nil)))) @@ -26,7 +26,7 @@ (heading-block/render "##### This is a heading block." nil))))) -(deftest settext-heading-text +(deftest settext-heading-renderer-text (testing "Settext heading block that's a H1" (is (= "<h1>This is a heading block.</h1>" (heading-block/render "This is a heading block.\n=========" nil)))) diff --git a/test/clarktown/renderers/horizontal_line_block_test.clj b/test/clarktown/renderers/horizontal_line_block_test.clj new file mode 100644 index 0000000..db72682 --- /dev/null +++ b/test/clarktown/renderers/horizontal_line_block_test.clj @@ -0,0 +1,13 @@ +(ns clarktown.renderers.horizontal-line-block-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.renderers.horizontal-line-block :as horizontal-line-block])) + + +(deftest horizontal-line-block-renderer-test + (testing "Creating a horizontal line" + (is (= (horizontal-line-block/render "***" nil) + "<hr>")) + + (is (= (horizontal-line-block/render "---" nil) + "<hr>")))) diff --git a/test/clarktown/parsers/inline_code_test.clj b/test/clarktown/renderers/inline_code_test.clj index 028c4b7..2071b7f 100644 --- a/test/clarktown/parsers/inline_code_test.clj +++ b/test/clarktown/renderers/inline_code_test.clj @@ -1,10 +1,10 @@ -(ns clarktown.parsers.inline-code-test +(ns clarktown.renderers.inline-code-test (:require [clojure.test :refer [deftest testing is]] - [clarktown.parsers.inline-code :as inline-code])) + [clarktown.renderers.inline-code :as inline-code])) -(deftest inline-code-test +(deftest inline-code-renderer-test (testing "Creating inline code text" (is (= "<code>This is inline code.</code>" (inline-code/render "`This is inline code.`" nil)))) diff --git a/test/clarktown/parsers/italic_test.clj b/test/clarktown/renderers/italic_test.clj index 8ab1369..29e7811 100644 --- a/test/clarktown/parsers/italic_test.clj +++ b/test/clarktown/renderers/italic_test.clj @@ -1,10 +1,10 @@ -(ns clarktown.parsers.italic-test +(ns clarktown.renderers.italic-test (:require [clojure.test :refer [deftest testing is]] - [clarktown.parsers.italic :as italic])) + [clarktown.renderers.italic :as italic])) -(deftest italic-test +(deftest italic-renderer-test (testing "Creating italic text with one surrounding asterisk character" (is (= "<em>This is italic.</em>" (italic/render "*This is italic.*" nil)))) diff --git a/test/clarktown/parsers/link_and_image_test.clj b/test/clarktown/renderers/link_and_image_test.clj index 348a8f9..aa821e3 100644 --- a/test/clarktown/parsers/link_and_image_test.clj +++ b/test/clarktown/renderers/link_and_image_test.clj @@ -1,10 +1,10 @@ -(ns clarktown.parsers.link-and-image-test +(ns clarktown.renderers.link-and-image-test (:require [clojure.test :refer [deftest testing is]] - [clarktown.parsers.link-and-image :as link-and-image])) + [clarktown.renderers.link-and-image :as link-and-image])) -(deftest link-test +(deftest link-renderer-test (testing "Creating a link" (is (= (link-and-image/render "[This is a link](https://example.com)" nil) "<a href=\"https://example.com\">This is a link</a>")) diff --git a/test/clarktown/renderers/quote_block_test.clj b/test/clarktown/renderers/quote_block_test.clj new file mode 100644 index 0000000..33a7495 --- /dev/null +++ b/test/clarktown/renderers/quote_block_test.clj @@ -0,0 +1,10 @@ +(ns clarktown.renderers.quote-block-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.renderers.quote-block :as quote-block])) + + +(deftest quote-block-block-renderer-test + (testing "Creating a quote block line" + (is (= (quote-block/render "> First line\n> second line" nil) + "<blockquote>First line\nsecond line</blockquote>"))))
\ No newline at end of file diff --git a/test/clarktown/parsers/strikethrough_test.clj b/test/clarktown/renderers/strikethrough_test.clj index fdf6188..55493e0 100644 --- a/test/clarktown/parsers/strikethrough_test.clj +++ b/test/clarktown/renderers/strikethrough_test.clj @@ -1,10 +1,10 @@ -(ns clarktown.parsers.strikethrough-test +(ns clarktown.renderers.strikethrough-test (:require [clojure.test :refer [deftest testing is]] - [clarktown.parsers.strikethrough :as strikethrough])) + [clarktown.renderers.strikethrough :as strikethrough])) -(deftest strikethrough-test +(deftest strikethrough-renderer-test (testing "Creating strikethrough text" (is (= (strikethrough/render "~~This is strikethrough text.~~" nil) "<del>This is strikethrough text.</del>"))) |
