Skip to content

Commit 81d4053

Browse files
committed
build: Propagate xref information from earlier blog posts
This allows using `secref` and `seclink` to refer to other blog posts or sections within them.
1 parent e49b4c9 commit 81d4053

File tree

3 files changed

+87
-48
lines changed

3 files changed

+87
-48
lines changed

blog/build.rkt

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -80,37 +80,37 @@
8080
#'(make-scribble-post-dep 'mod-path)])
8181

8282
(define all-post-deps
83-
(map markdown-post '("2015-07-18-automatically-deploying-a-frog-powered-blog-to-github-pages.md"
84-
"2015-08-22-deploying-racket-applications-on-heroku.md"
85-
"2015-08-30-managing-application-configuration-with-envy.md"
86-
"2015-09-23-canonical-factories-for-testing-with-factory-girl-api.md"
87-
"2015-11-06-functionally-updating-record-types-in-elm.md"
88-
"2015-12-21-adts-in-typed-racket-with-macros.md"
89-
"2016-02-18-simple-safe-multimethods-in-racket.md"
90-
"2016-06-03-four-months-with-haskell.md"
91-
"2016-08-11-climbing-the-infinite-ladder-of-abstraction.md"
92-
"2016-08-24-understanding-the-npm-dependency-model.md"
93-
"2016-10-01-using-types-to-unit-test-in-haskell.md"
94-
"2017-01-02-rascal-a-haskell-with-more-parentheses.md"
95-
"2017-01-05-rascal-is-now-hackett-plus-some-answers-to-questions.md"
96-
"2017-04-28-lifts-for-free-making-mtl-typeclasses-derivable.md"
97-
"2017-05-27-realizing-hackett-a-metaprogrammable-haskell.md"
98-
"2017-06-29-unit-testing-effectful-haskell-with-monad-mock.md"
99-
"2017-08-12-user-programmable-infix-operators-in-racket.md"
100-
"2017-08-28-hackett-progress-report-documentation-quality-of-life-and-snake.md"
101-
"2017-10-27-a-space-of-their-own-adding-a-type-namespace-to-hackett.md"
102-
"2018-02-10-an-opinionated-guide-to-haskell-in-2018.md"
103-
"2018-04-15-reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket.md"
104-
"2018-09-13-custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions.md"
105-
"2018-10-06-macroexpand-anywhere-with-local-apply-transformer.md"
106-
"2019-04-21-defeating-racket-s-separate-compilation-guarantee.md"
107-
"2019-09-07-demystifying-monadbasecontrol.md"
108-
"2019-10-19-empathy-and-subjective-experience-in-programming-languages.md"
109-
"2019-11-05-parse-don-t-validate.md"
110-
"2020-01-19-no-dynamic-type-systems-are-not-inherently-more-open.md"
111-
"2020-08-13-types-as-axioms-or-playing-god-with-static-types.md"
112-
"2020-11-01-names-are-not-type-safety.md"
113-
"2021-03-25-an-introduction-to-typeclass-metaprogramming.md")))
83+
(list (markdown-post "2015-07-18-automatically-deploying-a-frog-powered-blog-to-github-pages.md")
84+
(markdown-post "2015-08-22-deploying-racket-applications-on-heroku.md")
85+
(markdown-post "2015-08-30-managing-application-configuration-with-envy.md")
86+
(markdown-post "2015-09-23-canonical-factories-for-testing-with-factory-girl-api.md")
87+
(markdown-post "2015-11-06-functionally-updating-record-types-in-elm.md")
88+
(markdown-post "2015-12-21-adts-in-typed-racket-with-macros.md")
89+
(markdown-post "2016-02-18-simple-safe-multimethods-in-racket.md")
90+
(markdown-post "2016-06-03-four-months-with-haskell.md")
91+
(markdown-post "2016-08-11-climbing-the-infinite-ladder-of-abstraction.md")
92+
(markdown-post "2016-08-24-understanding-the-npm-dependency-model.md")
93+
(markdown-post "2016-10-01-using-types-to-unit-test-in-haskell.md")
94+
(markdown-post "2017-01-02-rascal-a-haskell-with-more-parentheses.md")
95+
(markdown-post "2017-01-05-rascal-is-now-hackett-plus-some-answers-to-questions.md")
96+
(markdown-post "2017-04-28-lifts-for-free-making-mtl-typeclasses-derivable.md")
97+
(markdown-post "2017-05-27-realizing-hackett-a-metaprogrammable-haskell.md")
98+
(markdown-post "2017-06-29-unit-testing-effectful-haskell-with-monad-mock.md")
99+
(markdown-post "2017-08-12-user-programmable-infix-operators-in-racket.md")
100+
(markdown-post "2017-08-28-hackett-progress-report-documentation-quality-of-life-and-snake.md")
101+
(markdown-post "2017-10-27-a-space-of-their-own-adding-a-type-namespace-to-hackett.md")
102+
(markdown-post "2018-02-10-an-opinionated-guide-to-haskell-in-2018.md")
103+
(markdown-post "2018-04-15-reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket.md")
104+
(markdown-post "2018-09-13-custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions.md")
105+
(markdown-post "2018-10-06-macroexpand-anywhere-with-local-apply-transformer.md")
106+
(markdown-post "2019-04-21-defeating-racket-s-separate-compilation-guarantee.md")
107+
(markdown-post "2019-09-07-demystifying-monadbasecontrol.md")
108+
(markdown-post "2019-10-19-empathy-and-subjective-experience-in-programming-languages.md")
109+
(markdown-post "2019-11-05-parse-don-t-validate.md")
110+
(markdown-post "2020-01-19-no-dynamic-type-systems-are-not-inherently-more-open.md")
111+
(markdown-post "2020-08-13-types-as-axioms-or-playing-god-with-static-types.md")
112+
(markdown-post "2020-11-01-names-are-not-type-safety.md")
113+
(markdown-post "2021-03-25-an-introduction-to-typeclass-metaprogramming.md")))
114114

115115
(define (timestamp-string)
116116
(define (pad n) (~r n #:min-width 2 #:pad-string "0"))
@@ -162,17 +162,25 @@
162162

163163
render-result)
164164

165-
(define (build-post-body dep)
165+
(define (build-post-body dep #:xrefs-in xref-in-paths)
166166
(define src-mod-time (file-or-directory-modify-seconds (post-dep-src-path dep) #f (λ () #f)))
167167
(define info-mod-time (file-or-directory-modify-seconds (post-dep-info-path dep) #f (λ () #f)))
168168
(cond
169+
; Note: this check doesn’t handle the case where we ought to rebuild because
170+
; the xrefs-in changed. This is because we don’t currently implement
171+
; dependency tracking for blog posts, so we’d have to pessimistically
172+
; rebuild *all* newer blog posts whenever an older one changed. This seems
173+
; okay for now, since older blog posts don’t change very often, and even
174+
; when they do, they usually don’t change in ways that would invalidate
175+
; external references.
169176
[(and src-mod-time info-mod-time (> info-mod-time src-mod-time))
170177
(deserialize (call-with-input-file* (post-dep-info-path dep) read))]
171178
[else
172179
(eprintf "~a running <posts>/~a\n" (timestamp-string) (find-relative-path posts-dir (post-dep-src-path dep)))
173180
(render-scribble blog-post-render%
174181
(post-dep-main-part dep)
175182
(post-dep-info-path dep)
183+
#:xrefs-in xref-in-paths
176184
#:xref-out (post-dep-xref-path dep))]))
177185

178186
(define (build-post-page info #:older older-info #:newer newer-info)
@@ -232,8 +240,17 @@
232240
(make-directory* output-dir)
233241

234242
(define all-posts
235-
(for/list ([dep (in-list all-post-deps)])
236-
(build-post-body dep)))
243+
(for/fold ([rendered-results '()]
244+
[prev-xrefs '()]
245+
#:result (reverse rendered-results))
246+
([dep (in-list all-post-deps)])
247+
; Currently, we only pass the xrefs from previous blog posts, which means
248+
; circular references between blog posts is impossible. Supporting that
249+
; would require a more elaborate strategy, and it isn’t necessary at the
250+
; moment, so we stick to the simple thing for now.
251+
(values (cons (build-post-body dep #:xrefs-in prev-xrefs) rendered-results)
252+
(cons (post-dep-xref-path dep) prev-xrefs))))
253+
237254
(for ([post (in-list all-posts)]
238255
[older-post (in-list (cons #f all-posts))]
239256
[newer-post (in-list (append (rest all-posts) (list #f)))])

blog/lang/base.rkt

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
#:style (or/c style? symbol? (listof symbol?) #f)]
3838
#:rest (listof pre-content?)
3939
part-start?)]
40+
[secref (->* [string?]
41+
[#:doc (or/c module-path? #f)
42+
#:post (or/c string? #f)
43+
#:tag-prefixes (or/c (listof string?) #f)]
44+
element?)]
4045
[seclink (->* [string?]
4146
[#:doc (or/c module-path? #f)
4247
#:post (or/c string? #f)
@@ -45,6 +50,7 @@
4550
#:rest (listof pre-content?)
4651
element?)]
4752
[other-post (-> string? element?)]
53+
[other-post* (-> string? pre-content? ... element?)]
4854

4955
[code (-> content? ... element?)]
5056
[code-block (-> content? ... block?)]
@@ -68,7 +74,7 @@
6874

6975
(module* lang racket/base
7076
(require scribble/doclang2
71-
(except-in scribble/base seclink section)
77+
(except-in scribble/base seclink secref section)
7278
(submod ".."))
7379
(provide (all-from-out scribble/doclang2
7480
scribble/base
@@ -106,8 +112,19 @@
106112
#:style [style #f]
107113
#:depth [depth 0]
108114
. pre-content)
109-
(~> (apply scribble:section pre-content
110-
#:tag tag #:tag-prefix prefix #:style style)
115+
(define content (decode-content pre-content))
116+
(~> (apply scribble:section content
117+
; `scribble:section` already handles generating a tag from the
118+
; decoded content if necessary, but it preprocesses it by replacing
119+
; most non-ASCII characters with underscores. This is to avoid
120+
; causing trouble in generated URLs, but it’s arguably the wrong
121+
; layer at which to do this escaping, and indeed, we have our own
122+
; scheme for generating anchor names from tags in the renderer.
123+
; So if no tag is provided, we handle generating one ourselves
124+
; here, without any escaping.
125+
#:tag (or tag (content->string content))
126+
#:tag-prefix prefix
127+
#:style style)
111128
(struct-copy part-start _ [depth depth])))
112129

113130
(define (subsubsubsection #:tag [tag #f]
@@ -117,6 +134,17 @@
117134
(apply section pre-content #:depth 3
118135
#:tag tag #:tag-prefix prefix #:style style))
119136

137+
(define (secref tag
138+
#:doc [module-path #f]
139+
#:post [post-path #f]
140+
#:tag-prefixes [prefixes #f])
141+
(scribble:secref
142+
tag
143+
#:doc module-path
144+
#:tag-prefixes (if post-path
145+
(cons (blog-post-path->tag-prefix post-path) (or prefixes '()))
146+
prefixes)))
147+
120148
(define (seclink tag
121149
#:doc [module-path #f]
122150
#:post [post-path #f]
@@ -131,7 +159,9 @@
131159
#:indirect? indirect?))
132160

133161
(define (other-post post-path)
134-
(seclink "top" #:post post-path))
162+
(secref "top" #:post post-path))
163+
(define (other-post* post-path . pre-content)
164+
(apply seclink "top" #:post post-path pre-content))
135165

136166
;; -----------------------------------------------------------------------------
137167

blog/markdown.rkt

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
racket/list
88
racket/match
99
racket/string
10-
scribble/base
10+
(except-in scribble/base section secref seclink)
1111
scribble/core
1212
scribble/decode
1313
scribble/decode-struct
@@ -17,15 +17,7 @@
1717
cdata
1818
string->xexpr)
1919

20-
(only-in "lang/base.rkt"
21-
code
22-
code-block
23-
footnote-collect-element
24-
footnote-ref
25-
footnotes-section
26-
post-date
27-
post-tags
28-
pygments-block))
20+
"lang/base.rkt")
2921

3022
(provide (contract-out
3123
[parse-markdown-post (-> input-port? part?)]))

0 commit comments

Comments
 (0)