commit 11d40133c39ce5fc163733c691027a011da06f96
parent 4607be8cdb1cb7445fdf9c18e4515f7803e827a8
Author: Daniel Barlow <dan@telent.net>
Date: Wed, 16 Jul 2025 20:54:56 +0100
list markup
this approach works for homogenous lists but will break if
there's a ul inside an ol or vice versa
Diffstat:
| M | textile.fnl | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
1 file changed, 66 insertions(+), 4 deletions(-)
diff --git a/textile.fnl b/textile.fnl
@@ -9,6 +9,10 @@
nil
(table.concat lines "")))))
+(fn cons-tag [tag content]
+ (doto content
+ (table.insert 1 tag)))
+
(define-tests :read-blocks
(with-open [f (io.open "tests/paras.txt" :r)]
(let [paras
@@ -64,8 +68,7 @@
;; walk the string from left to right. on encountering
;; any recognised opening token for inline markup, find the
;; matching closing token, and recursively walk the bit in the
-;; middle. we have to find the first occurence of _any_ opening
-;; token instead
+;; middle. Note that markup may be nested
(fn scan-tok [endtok tag]
(fn [f str]
@@ -129,9 +132,68 @@
["john " [:em "paul " [:strong "george"]]])
(expect= (inline-markup "see \"a link\":https://www.example.com for more")
- ["see " [["a" {:href "https://www.example.com"}] "a link"] ["for more"]]
- )
+ ["see " [["a" {:href "https://www.example.com"}] "a link"] ["for more"]])
+ )
+
+;; a list item starts with one or more * or # in the left-hand column and
+;; continues until there is another list item of the same type (ul/ol)
+;; nesting level, or until the end of the block
+
+;; a list item may contain another list
+
+;; there need not be a blank line before the list
+
+(fn wrap-list-item [tok content]
+ (let [list-type (if (= (string.sub tok 1 1) "#") :ol :ul)
+ inmost? (= (# tok) 1)]
+ (if inmost?
+ [list-type [:li content]]
+ [list-type [:li
+ (wrap-list-item (string.sub tok 2) content)]])))
+
+(fn list-append [a1 a2]
+ (icollect [_ v (ipairs a2) &into a1] v))
+
+
+(fn collapse-lists [lists]
+ (case lists
+ [[tag1 & els1] [tag1 & els2] & more]
+ (do
+ (collapse-lists
+ (cons-tag
+ (cons-tag tag1 (list-append els1 els2))
+ more)))
+ [first & rest]
+ (cons-tag first (collapse-lists rest))
+ [] []
+ ))
+
+
+(fn extract-lists [block]
+ (.
+ (collapse-lists
+ (icollect [(tok content) (string.gmatch block "([%#%*]+) *([^%#%*]+)")]
+ (wrap-list-item tok content)))
+ 1))
+
+
+(define-tests :extract-lists
+ (expect= (extract-lists "* item 1\n* item 2\n*item 3\n")
+ [:ul [:li "item 1\n"] [:li "item 2\n"] [:li "item 3\n"]])
+
+ (expect= (extract-lists "* item 1\n** subitem\n*item 2\n")
+ [:ul [:li "item 1\n"] [:li [:ul [:li "subitem\n"]]] [:li "item 2\n"]])
+
+ (expect= (extract-lists "# item 1\n# item 2\n## subitem\n")
+ [:ol [:li "item 1\n"] [:li "item 2\n"] [:li [:ol [:li "subitem\n"]]]])
+
+ (expect= (extract-lists "* item 1\n** subitem\n*item 2\n")
+ [:ul [:li "item 1\n"] [:li [:ul [:li "subitem\n"]]] [:li "item 2\n"]])
+
+
)
+
+
{}