Остання активність 21 hours ago

Версія b3e02346fb18d2e5d379a554e57e0f7a9878562b

snarl.factor Неформатований Playground
1! Copyright (C) 2026 CapitalEx
2! This mine and only mine all rights reserved >:3c
3USING: accessors assocs assocs.extras combinators
4combinators.short-circuit command-line continuations debugger
5html.elements io io.directories io.encodings.utf8 io.files
6io.files.info io.pathnames io.streams.string kernel literals
7namespaces sbufs sequences sequences.deep sets sorting splitting
8strings unicode xml xml.data xml.state xml.traversal xml.writer
9;
10FROM: namespaces => set ;
11IN: snarl
12<PRIVATE
13M: xml write-xml
14 [ before>> write-xml ]
15 [ body>> write-xml ]
16 [ after>> write-xml ] tri ;
17
18SYMBOLS: @current-page @public-path @template @pages @titles
19 @inbound @headers @hidden-from-sitemap ;
20
21CONSTANT: HEADERS $[
22 { "h1" "h2" "h3" "h4" "h5" "h6" } [ <null-name> ] map!
23]
24
25: set-html-escape-codes ( -- )
26 H{
27 { "emsp" { 8195 } }
28 { "nbsp" { 160 } }
29 { "amp" { 38 } }
30 { "mdash" { 8212 } }
31 } extra-entities set ;
32
33: current-page ( -- current-page ) @current-page get ; inline
34: inbound ( -- inbound ) @inbound get ; inline
35: titles ( -- titles ) @titles get ; inline
36: pages ( -- pages ) @pages get ; inline
37: template ( -- template ) @template get ; inline
38: headers ( -- headers ) @headers get ; inline
39
40: <section ( -- ) "<section" write-html ;
41: section> ( -- ) ">" write-html ;
42: </section> ( -- ) "</section>" write-html ;
43
44: set-main-article ( xml -- xml )
45 dup "main-article" "id" set-attr ;
46
47: article? ( string -- string )
48 >lower { [ "the" = ] [ "a" = ] [ "an" = ] } 1|| ;
49
50: leading-letter ( words -- words )
51 split-words [ article? ] reject first 1 head >upper ;
52
53: group-pages-alphbetically ( -- pages )
54 pages keys [ titles at leading-letter ] collect-by
55 [ [ titles at ] sort-by ] assoc-map sort-keys ;
56
57: find-headers ( tag -- headers )
58 [ HEADERS swap '[ _ tag-named? ] any? ] { } deep-filter-as ;
59
60: backlink ( links -- )
61 current-page inbound '[ _ _ swapd adjoin-at ] each ;
62
63: bad-link? ( link -- ? )
64 { [ empty? ] [ "http" head? ]
65 [ "sitemap.html" = ] [ current-page = ] } 1|| ;
66
67: links ( xml -- links )
68 "a" deep-tags-named [ "href" attr ] map! ;
69
70: link-outbound ( xml -- xml )
71 dup links [ bad-link? ] reject [ "#" split first ] map backlink ;
72
73: find-title ( xml -- title )
74 "h1" deep-tag-named deep-children>string ;
75
76: load-templates ( wiki-path -- )
77 "_templates/page.html" append-path utf8 file-contents >sbuf
78 @template set ;
79
80: save-headers ( xml -- xml )
81 dup find-headers current-page headers set-at ;
82
83: save-title ( xml -- xml )
84 dup current-page titles [ drop find-title ] change-at ;
85
86: save-page ( xml -- xml )
87 dup current-page @pages get set-at ;
88
89: ?hide-from-sitemap ( xml -- xml )
90 dup "sitemap" attr [
91 current-page @hidden-from-sitemap get adjoin
92 ] when ;
93
94: write-article ( template page xml -- template )
95 nip set-main-article xml>string "{{article}}" swap replace ;
96
97: list-inbound ( inbound -- )
98 [ <li> <a dup =href a> titles at write </a> </li> ] each ;
99
100: write-inbound ( template page xml -- template )
101 drop inbound at members [ titles at leading-letter ] sort-by [
102 <h4 "inbound-links" =id h4> "In Bound Links" write </h4>
103 <ul "link-list" =id ul> list-inbound </ul>
104 ] with-string-writer "{{backlinks}}" swap replace ;
105
106: list-headers ( headers -- )
107 [
108 <li> <a dup "id" attr "#" prepend =href a>
109 deep-children>string write
110 </a> </li>
111 ] each ;
112
113: write-headers ( template page xml -- template )
114 drop headers at [ "id" attr ] filter [
115 <h4> "Headers" write </h4>
116 <ul "header-list" =id ul> list-headers </ul>
117 ] with-string-writer "{{headings}}" swap replace ;
118
119: write-title ( template page xml -- template )
120 drop titles at "{{title}}" swap replace ;
121
122: write-locations ( pages -- )
123 [ <li> <a dup =href a> titles at write </a> </li> ] each ;
124
125: reject-hidden-pages ( pages -- pages )
126 [ @hidden-from-sitemap get in? ] reject ;
127
128: remove-hidden-pages ( sitemap -- sitemap )
129 [ reject-hidden-pages ] map-values harvest-values ;
130
131: render-sitemap ( -- string )
132 group-pages-alphbetically remove-hidden-pages [
133 <section "sitemap" =id section>
134 <h1> "Site Map" write </h1>
135 [
136 <section "listing" =class section>
137 <h3> swap write </h3>
138 <ul> write-locations </ul>
139 </section>
140 ] assoc-each
141 </section>
142 ] with-string-writer ;
143
144: render-page ( page xml -- string )
145 [ dup @current-page set ] dip [ template ] 2dip {
146 [ write-title ] [ write-headers ]
147 [ write-inbound ] [ write-article ]
148 } 2cleave ;
149
150: process-page ( xml -- )
151 save-page save-title save-headers
152 ?hide-from-sitemap link-outbound drop ;
153
154: load-page ( file-path -- )
155 dup file-name @current-page set file>xml process-page ;
156
157: should-write? ( to-write path -- ? )
158 dup file-exists? [ utf8 file-contents swap >string = not ] [ 2drop t ] if ;
159
160: write-page ( file string -- )
161 swap @public-path get prepend-path
162 2dup should-write? [ utf8 set-file-contents ] [ 2drop ] if ;
163
164: write-pages ( vec -- )
165 [ write-page ] assoc-each ;
166
167: generate-sitemap ( vec -- vec )
168 template
169 "Site Map" "{{title}}" swap replace
170 "" "{{headings}}" swap replace
171 "" "{{backlinks}}" swap replace
172 render-sitemap "{{article}}" swap replace
173 "sitemap.html" swap set-of ;
174
175: generate-pages ( -- vec )
176 pages [ [ render-page ] keepd swap ] assoc-map ;
177
178: load-articles ( pages-path -- )
179 qualified-directory-files
180 [ directory? ] reject
181 [ load-page ] each ;
182
183: initialize-paths ( args -- pages-path )
184 {
185 [ "public" append-path @public-path set ]
186 [ load-templates ]
187 [ "pages" append-path ]
188 } cleave ;
189
190: initialize-state ( -- )
191 H{ } clone @titles set H{ } clone @headers set
192 H{ } clone @inbound set H{ } clone @pages set
193 HS{ } clone @hidden-from-sitemap set ;
194
195: build-failed ( _ error -- )
196 nip print-error
197 "Failed to build: " write current-page print ;
198
199PRIVATE>
200
201: build-wiki ( args -- )
202 [ set-html-escape-codes
203 initialize-state
204 initialize-paths
205 load-articles
206 generate-pages
207 generate-sitemap
208 write-pages ] [ build-failed ] recover ;
209
210MAIN: [
211 command-line get ?first
212 [ build-wiki ] [ "Usage: snarl DIR" print ] if*
213]