summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--src/clarktown/core.clj151
-rw-r--r--src/clarktown/correctors.clj17
-rw-r--r--src/clarktown/correctors/atx_heading_block.clj19
-rw-r--r--src/clarktown/correctors/code_block.clj27
-rw-r--r--src/clarktown/matchers/code_block.clj10
-rw-r--r--src/clarktown/matchers/empty_block.clj (renamed from src/clarktown/parsers/empty_block.clj)12
-rw-r--r--src/clarktown/matchers/heading_block.clj29
-rw-r--r--src/clarktown/matchers/horizontal_line_block.clj10
-rw-r--r--src/clarktown/matchers/list_block.clj10
-rw-r--r--src/clarktown/matchers/quote_block.clj11
-rw-r--r--src/clarktown/parser.clj195
-rw-r--r--src/clarktown/parsers.clj136
-rw-r--r--src/clarktown/parsers/horizontal_line_block.clj16
-rw-r--r--src/clarktown/renderers/bold.clj (renamed from src/clarktown/parsers/bold.clj)2
-rw-r--r--src/clarktown/renderers/code_block.clj (renamed from src/clarktown/parsers/code_block.clj)9
-rw-r--r--src/clarktown/renderers/empty_block.clj7
-rw-r--r--src/clarktown/renderers/heading_block.clj (renamed from src/clarktown/parsers/heading_block.clj)40
-rw-r--r--src/clarktown/renderers/horizontal_line_block.clj7
-rw-r--r--src/clarktown/renderers/inline_code.clj (renamed from src/clarktown/parsers/inline_code.clj)2
-rw-r--r--src/clarktown/renderers/italic.clj (renamed from src/clarktown/parsers/italic.clj)2
-rw-r--r--src/clarktown/renderers/link_and_image.clj (renamed from src/clarktown/parsers/link_and_image.clj)2
-rw-r--r--src/clarktown/renderers/list_block.clj (renamed from src/clarktown/parsers/list_block.clj)10
-rw-r--r--src/clarktown/renderers/paragraph_block.clj (renamed from src/clarktown/parsers/paragraph_block.clj)2
-rw-r--r--src/clarktown/renderers/quote_block.clj (renamed from src/clarktown/parsers/quote_block.clj)10
-rw-r--r--src/clarktown/renderers/strikethrough.clj (renamed from src/clarktown/parsers/strikethrough.clj)2
-rw-r--r--test/clarktown/matchers/empty_block_test.clj9
-rw-r--r--test/clarktown/matchers/horizontal_line_block_test.clj14
-rw-r--r--test/clarktown/matchers/quote_block_test.clj11
-rw-r--r--test/clarktown/parsers/empty_block_test.clj14
-rw-r--r--test/clarktown/parsers/horizontal_line_block_test.clj21
-rw-r--r--test/clarktown/parsers/quote_block_test.clj15
-rw-r--r--test/clarktown/renderers/bold_test.clj (renamed from test/clarktown/parsers/bold_test.clj)6
-rw-r--r--test/clarktown/renderers/code_block_test.clj (renamed from test/clarktown/parsers/code_block_test.clj)6
-rw-r--r--test/clarktown/renderers/empty_block_test.clj10
-rw-r--r--test/clarktown/renderers/heading_block_test.clj (renamed from test/clarktown/parsers/heading_block_test.clj)8
-rw-r--r--test/clarktown/renderers/horizontal_line_block_test.clj13
-rw-r--r--test/clarktown/renderers/inline_code_test.clj (renamed from test/clarktown/parsers/inline_code_test.clj)6
-rw-r--r--test/clarktown/renderers/italic_test.clj (renamed from test/clarktown/parsers/italic_test.clj)6
-rw-r--r--test/clarktown/renderers/link_and_image_test.clj (renamed from test/clarktown/parsers/link_and_image_test.clj)6
-rw-r--r--test/clarktown/renderers/quote_block_test.clj10
-rw-r--r--test/clarktown/renderers/strikethrough_test.clj (renamed from test/clarktown/parsers/strikethrough_test.clj)6
42 files changed, 495 insertions, 410 deletions
diff --git a/README.md b/README.md
index 1c96c82..cce655c 100644
--- a/README.md
+++ b/README.md
@@ -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>")))