|
23 | 23 | threading |
24 | 24 | (only-in xml write-xexpr xexpr/c) |
25 | 25 |
|
| 26 | + "../../lang/metadata.rkt" |
26 | 27 | "../../lang/post-language.rkt" |
27 | 28 | "../../paths.rkt" |
28 | 29 | "../metadata.rkt" |
|
37 | 38 | (implementation?/c render<%>))]) |
38 | 39 |
|
39 | 40 | blog-render-mixin |
40 | | - footnotes-render-mixin |
41 | 41 | pygments-render-mixin) |
42 | 42 |
|
43 | 43 | ;; ----------------------------------------------------------------------------- |
|
59 | 59 | (for/list ([(k v) (in-hash attrs)]) |
60 | 60 | (list k v))) |
61 | 61 |
|
62 | | -(define (style->tag-name+attributes s) |
| 62 | +(define (style->tag-name+attributes s ri) |
63 | 63 | (for/fold ([tag-name #f] |
64 | 64 | [attrs (if (string? (style-name s)) |
65 | 65 | (hasheq 'class (style-name s)) |
|
72 | 72 | (values (string->symbol name) attrs)] |
73 | 73 | [(attributes attrs*) |
74 | 74 | (values tag-name (attributes-union attrs (make-immutable-hasheq attrs*)))] |
| 75 | + [(link-target tag) |
| 76 | + (values tag-name (attributes-union attrs |
| 77 | + (hasheq 'id (tag->anchor-name (resolve-tag tag ri)))))] |
75 | 78 | [_ |
76 | 79 | (values tag-name attrs)]))) |
77 | 80 |
|
|
89 | 92 | (serializable-struct blog-page (title path) #:transparent |
90 | 93 | #:guard (struct-guard/c content? site-path?)) |
91 | 94 | (serializable-struct blog-page-anchor (title path anchor) #:transparent |
92 | | - #:guard (struct-guard/c content? site-path? string?)) |
| 95 | + #:guard (struct-guard/c (or/c content? #f) site-path? string?)) |
93 | 96 |
|
94 | 97 | (define header-depth->html-tag |
95 | 98 | (match-lambda |
|
217 | 220 | (get-current-site-path) |
218 | 221 | (tag->anchor-name (add-current-tag-prefix key))))))) |
219 | 222 |
|
| 223 | + (define/public (collect-link-target tag ci) |
| 224 | + (define key (generate-tag tag ci)) |
| 225 | + (collect-put! ci key (blog-page-anchor #f |
| 226 | + (get-current-site-path) |
| 227 | + (tag->anchor-name (add-current-tag-prefix key))))) |
| 228 | + |
| 229 | + (define/override (collect-nested-flow i ci) |
| 230 | + (cond |
| 231 | + [(findf link-target? (style-properties (nested-flow-style i))) |
| 232 | + => (λ (target) (collect-link-target (link-target-tag target) ci))]) |
| 233 | + (super collect-nested-flow i ci)) |
| 234 | + |
| 235 | + (define/override (collect-paragraph i ci) |
| 236 | + (cond |
| 237 | + [(findf link-target? (style-properties (paragraph-style i))) |
| 238 | + => (λ (target) (collect-link-target (link-target-tag target) ci))]) |
| 239 | + (super collect-paragraph i ci)) |
| 240 | + |
220 | 241 | (define/override (collect-target-element i ci) |
221 | | - (define key (generate-tag (target-element-tag i) ci)) |
222 | 242 | (when (redirect-target-element? i) |
223 | 243 | (raise-arguments-error 'collect-target-element "redirect targets not supported" |
224 | 244 | "element" i)) |
225 | | - (collect-put! ci key (blog-page-anchor #f |
226 | | - (get-current-site-path) |
227 | | - (tag->anchor-name (add-current-tag-prefix key))))) |
| 245 | + (collect-link-target (target-element-tag i) ci)) |
228 | 246 |
|
229 | 247 | (define/override (resolve-content i d ri) |
230 | 248 | (cond |
|
274 | 292 | ,@(append-map (λ~> (render-part ri)) (part-parts part))]) |
275 | 293 |
|
276 | 294 | (define/override (render-nested-flow i part ri starting-item?) |
277 | | - `[(blockquote ,@(super render-nested-flow i part ri starting-item?))]) |
| 295 | + (define-values [tag-name attrs] (style->tag-name+attributes (nested-flow-style i) ri)) |
| 296 | + `[(,(or tag-name 'blockquote) |
| 297 | + ,(attributes->list attrs) |
| 298 | + ,@(super render-nested-flow i part ri starting-item?))]) |
278 | 299 |
|
279 | 300 | (define/override (render-paragraph e part ri) |
280 | | - (define-values [tag-name attrs] (style->tag-name+attributes (paragraph-style e))) |
| 301 | + (define-values [tag-name attrs] (style->tag-name+attributes (paragraph-style e) ri)) |
281 | 302 | `[(,(or tag-name 'p) |
282 | 303 | ,(attributes->list attrs) |
283 | 304 | ,@(super render-paragraph e part ri))]) |
284 | 305 |
|
285 | 306 | (define/override (render-itemization e part ri) |
286 | 307 | (define style (itemization-style e)) |
287 | | - (define-values [tag-name attrs] (style->tag-name+attributes style)) |
| 308 | + (define-values [tag-name attrs] (style->tag-name+attributes style ri)) |
288 | 309 | `[(,(or tag-name |
289 | 310 | (if (eq? (style-name style) 'ordered) 'ol 'ul)) |
290 | 311 | ,(attributes->list attrs) |
|
305 | 326 | [(string? elem) (list elem)] |
306 | 327 | [else |
307 | 328 | (define style (normalize-element-style (if (element? elem) (element-style elem) #f))) |
308 | | - (define-values [tag-name attrs] (style->tag-name+attributes style)) |
| 329 | + (define-values [tag-name attrs] (style->tag-name+attributes style ri)) |
309 | 330 |
|
310 | 331 | (define (wrap-for-style rendered #:attrs [attrs attrs]) |
311 | 332 | ; First, we determine what element wrappers are needed by the style. |
|
361 | 382 | [(link-element _ _ tag) |
362 | 383 | (define indirect? (memq 'indirect-link (style-properties style))) |
363 | 384 | (define dest (and (not indirect?) (resolve-get part ri tag))) |
364 | | - (unless (or dest indirect?) |
365 | | - (error 'render "unknown link destination\n tag: ~e" tag)) |
366 | | - |
367 | 385 | (define href |
368 | 386 | (match dest |
369 | 387 | ; racket doc reference |
|
388 | 406 |
|
389 | 407 | (super-new))) |
390 | 408 |
|
391 | | -;; Adds support for footnote definitions and references to a renderer. |
392 | | -;; Specifically, the renderer is adjusted as follows: |
393 | | -;; |
394 | | -;; * An `element` with a `footnote-reference` style property is treated as a |
395 | | -;; /footnote references/. The content of footnote references is ignored, and |
396 | | -;; the element is replaced with a link to the corresponding footnote |
397 | | -;; definition. |
398 | | -;; |
399 | | -;; * A `nested-flow` with a `footnote-definition` style property is treated as |
400 | | -;; a /footnote definition/. During the render pass, footnote definitions are |
401 | | -;; removed from the document flow in `render-flow` and rendered separately. |
402 | | -;; The collected footnote definitions can be retrieved using the |
403 | | -;; `get-rendered-footnote-definitions` method in a subclassing renderer. |
404 | | -;; |
405 | | -;; * The `render-footnote-definition` method can be overridden to customize the |
406 | | -;; way footnote definitions are rendered. |
407 | | -(define (footnotes-render-mixin %) |
408 | | - (class/hijack % |
409 | | - #:hijack-methods [render-one] |
410 | | - |
411 | | - (define footnote-ids '()) |
412 | | - (define footnote-definitions '()) |
413 | | - |
414 | | - (define/override (start-collect ds fns ci) |
415 | | - (set! footnote-ids '()) |
416 | | - (super start-collect ds fns ci)) |
417 | | - |
418 | | - (define/override (collect-nested-flow b ci) |
419 | | - (cond |
420 | | - [(findf footnote-definition? (style-properties (nested-flow-style b))) |
421 | | - => (λ (defn) (set! footnote-ids (cons (footnote-definition-note-id defn) footnote-ids)))]) |
422 | | - (super collect-nested-flow b ci)) |
423 | | - |
424 | | - (define/public (get-rendered-footnote-definitions) |
425 | | - (reverse footnote-definitions)) |
426 | | - |
427 | | - (define/public-final (super-render-one part ri output-file) |
428 | | - (super render-one part ri output-file)) |
429 | | - (define/overment (render-one part ri output-file) |
430 | | - (set! footnote-ids (reverse footnote-ids)) |
431 | | - (set! footnote-definitions '()) |
432 | | - (inner/hijack render-one part ri output-file)) |
433 | | - |
434 | | - (define/override (render-content elem part ri) |
435 | | - (match elem |
436 | | - [(element (style #f (list (footnote-reference note-id))) '()) |
437 | | - (define note-index (and~> (index-of footnote-ids note-id) add1)) |
438 | | - `[(sup (a ([href ,(if note-index |
439 | | - (~a "#footnote-" note-index) |
440 | | - "#")]) |
441 | | - ,(or (~a note-index) "???")))]] |
442 | | - [_ (super render-content elem part ri)])) |
443 | | - |
444 | | - (define/override (render-flow blocks part ri starting-item?) |
445 | | - (define-values [footnote-blocks other-blocks] |
446 | | - (partition (λ (block) (and (nested-flow? block) |
447 | | - (~>> (nested-flow-style block) |
448 | | - style-properties |
449 | | - (memf footnote-definition?)))) |
450 | | - blocks)) |
451 | | - (for ([footnote-block (in-list footnote-blocks)]) |
452 | | - (define rendered (render-footnote-definition footnote-block part ri)) |
453 | | - (set! footnote-definitions (cons rendered footnote-definitions))) |
454 | | - (super render-flow other-blocks part ri starting-item?)) |
455 | | - |
456 | | - (define/public (render-footnote-definition block part ri) |
457 | | - (render-flow (nested-flow-blocks block) part ri #t)) |
458 | | - |
459 | | - (super-new))) |
460 | | - |
461 | 409 | ;; Recognizes `paragraph`s and `element`s with a `pygments-content` style |
462 | 410 | ;; property and replaces them with the result of running Pygments on the content |
463 | 411 | ;; in the property. Body content and other style properties are ignored. |
|
501 | 449 |
|
502 | 450 | ;; The renderer used for actual blog posts, with all the bells and whistles. |
503 | 451 | (define blog-post-render% |
504 | | - (class (pygments-render-mixin (footnotes-render-mixin (blog-render-mixin base-render%))) |
| 452 | + (class (pygments-render-mixin (blog-render-mixin base-render%)) |
505 | 453 | (inherit render-content |
506 | 454 | render-flow |
507 | | - render-part |
508 | | - get-rendered-footnote-definitions) |
| 455 | + render-part) |
509 | 456 |
|
510 | 457 | ; We don’t render blog posts directly to HTML because we need to be able to |
511 | 458 | ; render them in different ways on different pages, so we generate `.info` |
|
521 | 468 | (define props (style-properties (part-style part))) |
522 | 469 | (define title-str (content->string (strip-aux (part-title-content part)) this part ri)) |
523 | 470 | (define title-content (render-content (part-title-content part) part ri)) |
524 | | - (define body-main-content (append (render-flow (part-blocks part) part ri #t) |
| 471 | + (define body-content (append (render-flow (part-blocks part) part ri #t) |
525 | 472 | (append-map (λ~> (render-part ri)) (part-parts part)))) |
526 | | - (define footnotes-block |
527 | | - `(div ([class "footnotes"]) |
528 | | - (ol ,@(for/list ([footnote-element (in-list (get-rendered-footnote-definitions))] |
529 | | - [footnote-index (in-naturals 1)]) |
530 | | - `(li ([id ,(~a "footnote-" footnote-index)]) ,@footnote-element))))) |
531 | 473 | (define post |
532 | 474 | (rendered-post title-str |
533 | 475 | title-content |
534 | 476 | (get-required-style-prop post-date? props) |
535 | 477 | (post-tags-tags (get-required-style-prop post-tags? props)) |
536 | | - (append body-main-content (list footnotes-block)))) |
| 478 | + body-content)) |
537 | 479 | (write (serialize post)) |
538 | 480 | post) |
539 | 481 |
|
|
0 commit comments