diff options
author | Case Duckworth | 2023-03-29 21:21:14 -0500 |
---|---|---|
committer | Case Duckworth | 2023-03-29 21:21:14 -0500 |
commit | 6d7bbec15031d689022c8e5155b81c15784f7deb (patch) | |
tree | 530d40aaf0ad41cce80213895905211482a9fa62 | |
download | wikme-6d7bbec15031d689022c8e5155b81c15784f7deb.tar.gz wikme-6d7bbec15031d689022c8e5155b81c15784f7deb.zip |
Initial commit (oops)
-rw-r--r-- | src/wikme.scm | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/wikme.scm b/src/wikme.scm new file mode 100644 index 0000000..187864e --- /dev/null +++ b/src/wikme.scm | |||
@@ -0,0 +1,177 @@ | |||
1 | ;;; wikme --- build a static wiki out of a folder of markdown files | ||
2 | |||
3 | (import (cmark) | ||
4 | (srfi-152) | ||
5 | (utf8) | ||
6 | (chicken irregex) | ||
7 | (chicken port) | ||
8 | (chicken string)) | ||
9 | |||
10 | |||
11 | ;;; Configuration | ||
12 | |||
13 | (define site-config | ||
14 | (make-parameter `((base-url . "https://www.example.com") | ||
15 | ;; These default directories aren't .. great. | ||
16 | (source-dir . "src") | ||
17 | (output-dir . "out") | ||
18 | (transformers . ,(list commonmark->html | ||
19 | wikify-links)) | ||
20 | (filename-transform | ||
21 | . (lambda (fname) | ||
22 | (md->index-html fname))) | ||
23 | (page-environment | ||
24 | . ((title | ||
25 | . ,(lambda (page) | ||
26 | (cdr (assq 'title (page-meta page))))) | ||
27 | (body | ||
28 | . ,(lambda (page) | ||
29 | (page-body page))) | ||
30 | (last_updated | ||
31 | . ,(lambda (page) | ||
32 | (cdr (assq 'last-updated (page-meta page)))))))))) | ||
33 | |||
34 | (define (config-get x) | ||
35 | (if (assq x (site-config)) | ||
36 | (cdr (assq x (site-config))) | ||
37 | #f)) | ||
38 | |||
39 | |||
40 | ;;; Templates | ||
41 | |||
42 | (define (render template env) | ||
43 | ;;; Render TEMPLATE using ENV. | ||
44 | ;; TEMPLATE is a string with {{placeholders}}; ENV is an alist of key-value | ||
45 | ;; pairs to insert into the TEMPLATE's placeholders. | ||
46 | (string-substitute* template (env->replacements env))) | ||
47 | |||
48 | (define (env->replacements env) | ||
49 | ;;; Convert an ENV alist of the form `((X . Y) ...) to '(("{{X}}" . "Y") ...). | ||
50 | ;; X's are template variables and Y's are the values of those variables. In | ||
51 | ;; the template, both "{{X}}" and "{{ X }}" will be replaced. | ||
52 | ;; If Y is a thunk, call it. | ||
53 | (let loop ((env env) | ||
54 | (res '())) | ||
55 | (if (null? env) | ||
56 | res | ||
57 | (let* ((this (car env)) | ||
58 | (rest (cdr env)) | ||
59 | (key (->string (car this))) | ||
60 | (val (if (procedure? (cdr this)) | ||
61 | ((cdr this)) | ||
62 | (->string (cdr this))))) | ||
63 | (loop (cdr env) | ||
64 | (append (list (cons (string-append "{{" key "}}") val) | ||
65 | (cons (string-append "{{ " key " }}") val)) | ||
66 | env)))))) | ||
67 | |||
68 | |||
69 | ;;; Wiki links | ||
70 | |||
71 | (define wiki-link-sre | ||
72 | ;;; An SRE for [[wiki-style links|with optional titles]]. | ||
73 | '(: "[[" | ||
74 | (submatch-named page (+ (~ "|"))) | ||
75 | (? (submatch "|" (submatch-named title (*? nonl)))) | ||
76 | "]]")) | ||
77 | |||
78 | (define (wikify-links text) | ||
79 | ;;; Convert [[Wiki-style links]] to HTML style in TEXT. | ||
80 | (irregex-replace/all wiki-link-sre text | ||
81 | (lambda (m) | ||
82 | (let* ((page (irregex-match-substring m 'page)) | ||
83 | (title (or (irregex-match-substring m 'title) | ||
84 | page))) | ||
85 | (string-append | ||
86 | "<a href=\"" (linkify page) "\">" title "</a>"))))) | ||
87 | |||
88 | (define (linkify pagename) | ||
89 | ;;; Turn a page name into a link suitable for an <a> tag. | ||
90 | (string-append (base-url) "/" (slugify pagename) "/index.html")) | ||
91 | |||
92 | (define (string-capitalize str) | ||
93 | ;;; Capitalize the first word in STR, and ensure the rest of it is lowercase. | ||
94 | ;; Stolen and adapted from MIT/GNU Scheme | ||
95 | (let* ((end (string-length str)) | ||
96 | (str* (make-string end))) | ||
97 | (do ((i 0 (+ i 1))) | ||
98 | ((= i end)) | ||
99 | (string-set! str* i ((if (= i 0) char-upcase char-downcase) | ||
100 | (string-ref str i)))) | ||
101 | str*)) | ||
102 | |||
103 | (define (slugify str) | ||
104 | ;;; Convert STR to a 'slug', that is, another string suitable for linking. | ||
105 | ;; This function will return the input string, in sentence case, and with all | ||
106 | ;; punctuation and spaces converted to a hypen. | ||
107 | (string-capitalize | ||
108 | (string-trim-both (irregex-replace/all '(+ (~ alnum)) str "-") | ||
109 | (lambda (c) | ||
110 | (char=? c #\-))))) | ||
111 | |||
112 | (define (unslugify slug) | ||
113 | ;;; Convert a SLUG back into a normal string as best as possible. | ||
114 | ;; Because information is lost in slugification, it's impossible to be sure | ||
115 | ;; that the result of this procedure is totally accurate. That is, | ||
116 | ;; slugification is not round-trippable. | ||
117 | (irregex-replace/all '("-") slug " ")) | ||
118 | |||
119 | |||
120 | ;;; Transform source | ||
121 | |||
122 | (define (transform source . transformers) | ||
123 | ;;; Transform SOURCE to html by passing it through a series of TRANSFORMERS. | ||
124 | ;; Each TRANSFORMER should be a one-argument procedure taking and returning a | ||
125 | ;; string. | ||
126 | (let loop ((transformers transformers) | ||
127 | (output source)) | ||
128 | (if (null? transformers) | ||
129 | output | ||
130 | (loop (cdr transformers) | ||
131 | ((car transformers) output))))) | ||
132 | |||
133 | (define (md->index-html filename) | ||
134 | ;;; Transform a FILENAME of the form dir/name.md to dir/name/index.html. | ||
135 | ;; Uses source | ||
136 | ) | ||
137 | |||
138 | |||
139 | ;;; Pages | ||
140 | |||
141 | (define-record-type <page> | ||
142 | ;;; A wiki page is a mapping between source and body content, and between the | ||
143 | ;;; page's origin and its destination files, wrapped together with some | ||
144 | ;;; metadata. | ||
145 | (make-page source body origin destination meta) | ||
146 | page? | ||
147 | (source page-source ; source markup | ||
148 | (setter page-source)) | ||
149 | (body page-body ; rendered page body | ||
150 | (setter page-source)) | ||
151 | (origin page-origin ; file containing the markup | ||
152 | (setter page-origin)) | ||
153 | (destination page-destination ; destination file | ||
154 | (setter page-destination)) | ||
155 | (meta page-meta ; alist of metadata tags | ||
156 | (setter page-meta))) | ||
157 | |||
158 | (define (page-meta-ref key page) | ||
159 | ;;; Get metadata KEY from PAGE. | ||
160 | (cdr (assq key (page-meta page)))) | ||
161 | |||
162 | (define (file->page file | ||
163 | #!key | ||
164 | (transformers (config-get 'transformers)) | ||
165 | (destination )) | ||
166 | ;;; Create a <page> from FILE. | ||
167 | ;; Wraps make-page for easier use. | ||
168 | |||
169 | ) | ||
170 | |||
171 | |||
172 | ;;; Writing files | ||
173 | |||
174 | (define (publish file config) | ||
175 | ;;; Publish FILE, using CONFIG. | ||
176 | ;; CONFIG should be a configuration alist, which see above. | ||
177 | #f) | ||