summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAsko Nomm <asko@bien.ee>2022-04-06 13:43:59 +0200
committerAsko Nomm <asko@bien.ee>2022-04-06 13:43:59 +0200
commit2072d2730e0a7991bdf6474275f9fe9cd8fac182 (patch)
treef2f2e4190d38edafbac730487839adf356fa7149
parent3770175783d53f9008d81dca8b19741b218d925f (diff)
Fixes list blocks, which can now be of any depth and should work just fine.
-rw-r--r--.gitignore2
-rw-r--r--LICENSE.txt2
-rw-r--r--README.md26
-rw-r--r--project.clj8
-rw-r--r--src/clarktown/core.clj5
-rw-r--r--src/clarktown/parsers.clj14
-rw-r--r--src/clarktown/parsers/list_block.clj104
-rw-r--r--src/clarktown/parsers/ordered_list_block.clj25
8 files changed, 123 insertions, 63 deletions
diff --git a/.gitignore b/.gitignore
index 1eea1fd..6338a04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,6 @@ pom.xml.asc
.hg/
.clj-kondo/
.lsp/
+.idea/
+.calva/
test.md
diff --git a/LICENSE.txt b/LICENSE.txt
index 2508097..a11061f 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2021 Asko Nõmm
+Copyright (c) 2022 Asko Nõmm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index a0d6016..4a299bd 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,12 @@
-# clarktown
+# Clarktown
-A Clojure library designed to ... well, that part is up to you.
+A zero-dependency Markdown parser for Clojure projects.
-## Usage
+## Usage example
-FIXME
+```clojure
+(ns myapp.core
+ (:require [clarktown.core :as clarktown]))
-## License
-
-Copyright © 2021 FIXME
-
-This program and the accompanying materials are made available under the
-terms of the Eclipse Public License 2.0 which is available at
-http://www.eclipse.org/legal/epl-2.0.
-
-This Source Code may also be made available under the following Secondary
-Licenses when the conditions for such availability set forth in the Eclipse
-Public License, v. 2.0 are satisfied: GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or (at your
-option) any later version, with the GNU Classpath Exception which is available
-at https://www.gnu.org/software/classpath/license.html.
+(clarktown/render "**Hello, world!**")
+``` \ No newline at end of file
diff --git a/project.clj b/project.clj
index 284c054..65babb6 100644
--- a/project.clj
+++ b/project.clj
@@ -1,8 +1,8 @@
(defproject clarktown "1.0"
- :description "FIXME: write description"
- :url "http://example.com/FIXME"
- :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
- :url "https://www.eclipse.org/legal/epl-2.0/"}
+ :description "A zero-dependency, pure-clojure Markdown parser."
+ :url "https://github.com/askonomm/clarktown"
+ :license {:name "MIT"
+ :url "https://github.com/askonomm/clarktown/blob/master/LICENSE.txt"}
:dependencies [[org.clojure/clojure "1.11.0"]]
:plugins [[lein-auto "0.1.3"]]
:repl-options {:init-ns clarktown.core})
diff --git a/src/clarktown/core.clj b/src/clarktown/core.clj
index 3663c11..f192cc4 100644
--- a/src/clarktown/core.clj
+++ b/src/clarktown/core.clj
@@ -24,7 +24,4 @@
([markdown]
(render markdown parsers/parsers))
([markdown given-parsers]
- (parser/parse markdown given-parsers)))
-
-
-(render (slurp "/Users/asko/work/clarktown/test.md")) \ No newline at end of file
+ (parser/parse markdown given-parsers))) \ No newline at end of file
diff --git a/src/clarktown/parsers.clj b/src/clarktown/parsers.clj
index 01a6351..b5b6132 100644
--- a/src/clarktown/parsers.clj
+++ b/src/clarktown/parsers.clj
@@ -10,8 +10,7 @@
[clarktown.parsers.quote-block :as quote-block]
[clarktown.parsers.heading-block :as heading-block]
[clarktown.parsers.code-block :as code-block]
- [clarktown.parsers.unordered-list-block :as unordered-list-block]
- [clarktown.parsers.ordered-list-block :as ordered-list-block]
+ [clarktown.parsers.list-block :as list-block]
[clarktown.parsers.paragraph-block :as paragraph-block]))
@@ -31,20 +30,13 @@
:renderers [quote-block/render]}
{:matcher code-block/is?
:renderers [code-block/render]}
- {:matcher unordered-list-block/is?
+ {:matcher list-block/is?
:renderers [bold/render
italic/render
inline-code/render
strikethrough/render
link-and-image/render
- unordered-list-block/render]}
- {:matcher ordered-list-block/is?
- :renderers [bold/render
- italic/render
- inline-code/render
- strikethrough/render
- link-and-image/render
- ordered-list-block/render]}
+ list-block/render]}
{:renderers [bold/render
italic/render
inline-code/render
diff --git a/src/clarktown/parsers/list_block.clj b/src/clarktown/parsers/list_block.clj
new file mode 100644
index 0000000..9485f67
--- /dev/null
+++ b/src/clarktown/parsers/list_block.clj
@@ -0,0 +1,104 @@
+(ns clarktown.parsers.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\.|\*).*$")))
+
+
+(defn string->indent-n
+ [str]
+ (count (take-while #{\space} str)))
+
+
+(defn compose-items-with-indent-guides
+ [block]
+ (->> (string/split-lines block)
+ (mapv
+ (fn [line]
+ {:id (random-uuid)
+ :indent-n (string->indent-n line)
+ :value (-> line
+ string/trim)}))))
+
+
+(defn find-parent-id
+ [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
+ [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
+ [items item]
+ (->> items
+ (mapv
+ (fn [i]
+ (if (= (:id i) (:parent item))
+ (if (:items i)
+ (assoc i :items (concat (:items i) [item]))
+ (assoc i :items [item]))
+ (if (:items i)
+ (assoc i :items (add-to-parent (:items i) item))
+ i))))))
+
+
+(defn compose-item-tree
+ [block]
+ (loop [result []
+ items (compose-items-with-parents block)]
+ (if (empty? items)
+ result
+ (let [item (first items)
+ parent (:parent item)
+ new-item {:id (:id item)
+ :value (:value item)}]
+ (recur (if parent
+ (add-to-parent result item)
+ (concat result [new-item]))
+ (drop 1 items))))))
+
+
+(defn render-items
+ [items]
+ (loop [result ""
+ inner-items items]
+ (if (empty? inner-items)
+ (if (string/starts-with? (:value (first items)) "*")
+ (str "<ul>" result "</ul>")
+ (str "<ol>" result "</ol>"))
+ (let [inner-item (first inner-items)
+ value (if (string/starts-with? (:value inner-item) "*")
+ (-> (string/replace-first (:value inner-item) "*" "")
+ string/trim)
+ (-> (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 ordered list block"
+ [block _]
+ (-> (compose-item-tree block)
+ (render-items))) \ No newline at end of file
diff --git a/src/clarktown/parsers/ordered_list_block.clj b/src/clarktown/parsers/ordered_list_block.clj
deleted file mode 100644
index 4dfaf38..0000000
--- a/src/clarktown/parsers/ordered_list_block.clj
+++ /dev/null
@@ -1,25 +0,0 @@
-(ns clarktown.parsers.ordered-list-block
- (:require
- [clojure.string :as string]
- [clarktown.parser :as parser]))
-
-
-(defn is?
- "Determines whether we're dealing with a list block or not."
- [block]
- (re-matches #"(?s)^\d\..*$" (string/trim block)))
-
-
-(defn render
- "Renders the ordered list block"
- [block parsers]
- (loop [result ""
- items (string/split-lines block)]
- (if (empty? items)
- (str "<ol>" result "</ol>")
- (let [value (-> (first items)
- (string/replace-first #"\d\." "")
- string/trim
- (parser/parse parsers))]
- (recur (str result "<li>" value "</li>")
- (drop 1 items))))))