Skip to content

WIP: add option to output xexpr of html #498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

stevebyan
Copy link

@stevebyan stevebyan commented Apr 16, 2025

Output the body as an xexpr, and provide various metadata, including TOC links, in a struct rather than in html.

I've refactored html-render.rkt to optionally output the body of the document as an xexpr rather than as html, and provide document metadata including the TOC links in a struct rather than in html. My goal is to allow use of scribble's html in something like Greg Hendershott's Tadpole, and so I want to provide the document content in a form that can be applied to an xexpr template. I also want to provide the TOC links in a form that can be rendered by site templates using pure CSS to provide a responsive view of the navigation links.

I'm submitting this pull request to gather some feedback on my approach, before committing to doing more work to make this something actually mergeable into Scribble. To pull out the TOC as data rather than as html, I've duplicated a lot of code from the render-toc-view and render-onthispage-contents methods. A finished pull request would refactor render-toc-view and render-onthispage-contents to use the output of my list-of-toc-view and list-of-onthispage-contents. I'm also not sure at this point how I want to represent the TOC contents. Using structs would probably be better than the current hacked nested lists.

Is this a feature the Racket developers would consider including in Scribble? Except for the duplicated code, does my implementation approach look reasonable?

Output the body as an xexpr, and provide various metadata, including TOC links, in a struct rather than in html.
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resyntax analyzed 4 files in this pull request and has added suggestions.

Comment on lines +854 to +859
(map (lambda (p) (if (or (part-whole-page? p ri)
(and (part-style? p 'toc-hidden)
(all-toc-hidden? p)))
null
(flatten p prefixes #f)))
(part-parts d)))))))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map-to-for: This map operation can be replaced with a for/list loop.

Suggested change
(map (lambda (p) (if (or (part-whole-page? p ri)
(and (part-style? p 'toc-hidden)
(all-toc-hidden? p)))
null
(flatten p prefixes #f)))
(part-parts d)))))))
(for/list ([p (in-list (part-parts d))])
(if (or (part-whole-page? p ri)
(and (part-style? p 'toc-hidden) (all-toc-hidden? p)))
null
(flatten p prefixes #f))))))))
Debugging details
Textual replacement
(line-replacement
  #:new-lines
    '#("                  (for/list ([p (in-list (part-parts d))])"
       "                    (if (or (part-whole-page? p ri)"
       "                            (and (part-style? p 'toc-hidden) (all-toc-hidden? p)))"
       "                        null"
       "                        (flatten p prefixes #f))))))))")
  #:original-lines
    '#("                  (map (lambda (p) (if (or (part-whole-page? p ri) "
       "                                           (and (part-style? p 'toc-hidden)"
       "                                                (all-toc-hidden? p)))"
       "                                       null"
       "                                       (flatten p prefixes #f)))"
       "                       (part-parts d)))))))")
  #:start-line 854)
Syntactic replacement
(syntax-replacement
  #:introduction-scope #<procedure:...and/syntax-local.rkt:148:2>
  #:new-syntax
    #<syntax:/home/runner/.local/share/racket/8.17.0.1/pkgs/resyntax/default-recommendations/for-loop-shortcuts.rkt:149:2 (for/list ((p (in-list (part-parts d)))) (if (or (part-whole-page? p ri) (and (part-style? p (quote toc-hidden)) (all-toc-hidden? p))) null (flatten p prefixes #f)))>
  #:original-syntax
    #<syntax:scribble-lib/scribble/html-render.rkt:854:18 (map (lambda (p) (if (or (part-whole-page? p ri) (and (part-style? p (quote toc-hidden)) (all-toc-hidden? p))) null (flatten p prefixes #f))) (part-parts d))>
  #:source
    (file-source
     #<path:/home/runner/work/scribble/scribble/scribble-lib/scribble/html-render.rkt>))

Comment on lines +866 to +910
(map (lambda (p)
(let ([p (vector-ref p 0)]
[prefixes (vector-ref p 1)]
[from-d (vector-ref p 2)]
[add-tag-prefixes
(lambda (t prefixes)
(if (null? prefixes)
t
(cons (car t) (append prefixes (cdr t)))))])
(list
(if (part? p)
(format-number
(collected-info-number
(part-collected-info p ri))
null)
null)
(if (toc-element? p)
(render-content (toc-element-toc-content p)
from-d ri)
(parameterize ([current-no-links #t]
[extra-breaking? #t])
(list
(uri-unreserved-encode
(anchor-name
(add-tag-prefixes
(tag-key (if (part? p)
(car (part-tags/nonempty p))
(target-element-tag p))
ri)
prefixes)))
(cond
[(part? p) "tocsubseclink"]
[any-parts? "tocsubnonseclink"]
[else "tocsublink"])

(render-content
(if (part? p)
(strip-aux
(or (part-title-content p)
"???"))
(if (toc-target2-element? p)
(toc-target2-element-toc-content p)
(element-content p)))
from-d ri)))))))
ps)))))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map-to-for: This map operation can be replaced with a for/list loop.

Suggested change
(map (lambda (p)
(let ([p (vector-ref p 0)]
[prefixes (vector-ref p 1)]
[from-d (vector-ref p 2)]
[add-tag-prefixes
(lambda (t prefixes)
(if (null? prefixes)
t
(cons (car t) (append prefixes (cdr t)))))])
(list
(if (part? p)
(format-number
(collected-info-number
(part-collected-info p ri))
null)
null)
(if (toc-element? p)
(render-content (toc-element-toc-content p)
from-d ri)
(parameterize ([current-no-links #t]
[extra-breaking? #t])
(list
(uri-unreserved-encode
(anchor-name
(add-tag-prefixes
(tag-key (if (part? p)
(car (part-tags/nonempty p))
(target-element-tag p))
ri)
prefixes)))
(cond
[(part? p) "tocsubseclink"]
[any-parts? "tocsubnonseclink"]
[else "tocsublink"])
(render-content
(if (part? p)
(strip-aux
(or (part-title-content p)
"???"))
(if (toc-target2-element? p)
(toc-target2-element-toc-content p)
(element-content p)))
from-d ri)))))))
ps)))))
(for/list ([p (in-list ps)])
(let ([p (vector-ref p 0)]
[prefixes (vector-ref p 1)]
[from-d (vector-ref p 2)]
[add-tag-prefixes (lambda (t prefixes)
(if (null? prefixes)
t
(cons (car t) (append prefixes (cdr t)))))])
(list (if (part? p)
(format-number (collected-info-number (part-collected-info p ri)) null)
null)
(if (toc-element? p)
(render-content (toc-element-toc-content p) from-d ri)
(parameterize ([current-no-links #t]
[extra-breaking? #t])
(list (uri-unreserved-encode
(anchor-name (add-tag-prefixes
(tag-key (if (part? p)
(car (part-tags/nonempty p))
(target-element-tag p))
ri)
prefixes)))
(cond
[(part? p) "tocsubseclink"]
[any-parts? "tocsubnonseclink"]
[else "tocsublink"])
(render-content (if (part? p)
(strip-aux (or (part-title-content p) "???"))
(if (toc-target2-element? p)
(toc-target2-element-toc-content p)
(element-content p)))
from-d
ri)))))))))))
Debugging details
Textual replacement
(line-replacement
  #:new-lines
    '#("               (for/list ([p (in-list ps)])"
       "                 (let ([p (vector-ref p 0)]"
       "                       [prefixes (vector-ref p 1)]"
       "                       [from-d (vector-ref p 2)]"
       "                       [add-tag-prefixes (lambda (t prefixes)"
       "                                           (if (null? prefixes)"
       "                                               t"
       "                                               (cons (car t) (append prefixes (cdr t)))))])"
       "                   (list (if (part? p)"
       "                             (format-number (collected-info-number (part-collected-info p ri)) null)"
       "                             null)"
       "                         (if (toc-element? p)"
       "                             (render-content (toc-element-toc-content p) from-d ri)"
       "                             (parameterize ([current-no-links #t]"
       "                                            [extra-breaking? #t])"
       "                               (list (uri-unreserved-encode"
       "                                      (anchor-name (add-tag-prefixes"
       "                                                    (tag-key (if (part? p)"
       "                                                                 (car (part-tags/nonempty p))"
       "                                                                 (target-element-tag p))"
       "                                                             ri)"
       "                                                    prefixes)))"
       "                                     (cond"
       "                                       [(part? p) \"tocsubseclink\"]"
       "                                       [any-parts? \"tocsubnonseclink\"]"
       "                                       [else \"tocsublink\"])"
       "                                     (render-content (if (part? p)"
       "                                                         (strip-aux (or (part-title-content p) \"???\"))"
       "                                                         (if (toc-target2-element? p)"
       "                                                             (toc-target2-element-toc-content p)"
       "                                                             (element-content p)))"
       "                                                     from-d"
       "                                                     ri)))))))))))")
  #:original-lines
    '#("               (map (lambda (p)"
       "                      (let ([p (vector-ref p 0)]"
       "                            [prefixes (vector-ref p 1)]"
       "                            [from-d (vector-ref p 2)]"
       "                            [add-tag-prefixes"
       "                             (lambda (t prefixes)"
       "                               (if (null? prefixes)"
       "                                   t"
       "                                   (cons (car t) (append prefixes (cdr t)))))])"
       "                        (list"
       "                         (if (part? p)"
       "                             (format-number"
       "                              (collected-info-number"
       "                               (part-collected-info p ri))"
       "                              null)"
       "                             null)"
       "                         (if (toc-element? p)"
       "                             (render-content (toc-element-toc-content p)"
       "                                             from-d ri)"
       "                             (parameterize ([current-no-links #t]"
       "                                            [extra-breaking? #t])"
       "                               (list "
       "                                (uri-unreserved-encode"
       "                                 (anchor-name"
       "                                  (add-tag-prefixes"
       "                                   (tag-key (if (part? p)"
       "                                                (car (part-tags/nonempty p))"
       "                                                (target-element-tag p))"
       "                                            ri)"
       "                                   prefixes)))"
       "                                (cond"
       "                                  [(part? p) \"tocsubseclink\"]"
       "                                  [any-parts? \"tocsubnonseclink\"]"
       "                                  [else \"tocsublink\"])"
       "                                        "
       "                                (render-content"
       "                                 (if (part? p)"
       "                                     (strip-aux"
       "                                      (or (part-title-content p)"
       "                                          \"???\"))"
       "                                     (if (toc-target2-element? p)"
       "                                         (toc-target2-element-toc-content p)"
       "                                         (element-content p)))"
       "                                 from-d ri)))))))"
       "                    ps)))))")
  #:start-line 866)
Syntactic replacement
(syntax-replacement
  #:introduction-scope #<procedure:...and/syntax-local.rkt:148:2>
  #:new-syntax
    #<syntax:/home/runner/.local/share/racket/8.17.0.1/pkgs/resyntax/default-recommendations/for-loop-shortcuts.rkt:149:2 (for/list ((p (in-list ps))) (let ((p (vector-ref p 0)) (prefixes (vector-ref p 1)) (from-d (vector-ref p 2)) (add-tag-prefixes (lambda (t prefixes) (if (null? prefixes) t (cons (car t) (append prefixes (cdr t))))))) (list (if (part? p) (format-number (...>
  #:original-syntax
    #<syntax:scribble-lib/scribble/html-render.rkt:866:15 (map (lambda (p) (let ((p (vector-ref p 0)) (prefixes (vector-ref p 1)) (from-d (vector-ref p 2)) (add-tag-prefixes (lambda (t prefixes) (if (null? prefixes) t (cons (car t) (append prefixes (cdr t))))))) (list (if (part? p) (format-number (collected-in...>
  #:source
    (file-source
     #<path:/home/runner/work/scribble/scribble/scribble-lib/scribble/html-render.rkt>))

Comment on lines +1165 to +1181
(if xexpr-out?
(let ([article
(scribble-xexpr-page
title-string ; title
(authors-list) ; authors
(extract-date d) ; date
(extract-version d) ; document version
null ; tags
(unless (part-style? d 'no-toc+aux)
(list-of-toc-view d ri)) ; tocset
article-xexpr ; article
)]
[article-string (open-output-string)])
(begin
(write article article-string)
(displayln (program-format (get-output-string article-string) #:width 72))))
(xml:write-xexpr part-xexpr))))))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if-let-to-cond: cond with internal definitions is preferred over if with let, to reduce nesting

Suggested change
(if xexpr-out?
(let ([article
(scribble-xexpr-page
title-string ; title
(authors-list) ; authors
(extract-date d) ; date
(extract-version d) ; document version
null ; tags
(unless (part-style? d 'no-toc+aux)
(list-of-toc-view d ri)) ; tocset
article-xexpr ; article
)]
[article-string (open-output-string)])
(begin
(write article article-string)
(displayln (program-format (get-output-string article-string) #:width 72))))
(xml:write-xexpr part-xexpr))))))
(cond
[xexpr-out?
(define article
(scribble-xexpr-page title-string ; title
(authors-list) ; authors
(extract-date d) ; date
(extract-version d) ; document version
null ; tags
(unless (part-style? d 'no-toc+aux)
(list-of-toc-view d ri)) ; tocset
article-xexpr ; article
))
(define article-string (open-output-string))
(begin
(write article article-string)
(displayln (program-format (get-output-string article-string) #:width 72)))]
[else (xml:write-xexpr part-xexpr)])))))
Debugging details
Textual replacement
(line-replacement
  #:new-lines
    '#("            (cond"
       "              [xexpr-out?"
       "               (define article"
       "                 (scribble-xexpr-page title-string ; title"
       "                                      (authors-list) ; authors"
       "                                      (extract-date d) ; date"
       "                                      (extract-version d) ; document version"
       "                                      null ; tags"
       "                                      (unless (part-style? d 'no-toc+aux)"
       "                                        (list-of-toc-view d ri)) ; tocset"
       "                                      article-xexpr ; article"
       "                                      ))"
       "               (define article-string (open-output-string))"
       "               (begin"
       "                 (write article article-string)"
       "                 (displayln (program-format (get-output-string article-string) #:width 72)))]"
       "              [else (xml:write-xexpr part-xexpr)])))))")
  #:original-lines
    '#("            (if xexpr-out?"
       "                (let ([article"
       "                       (scribble-xexpr-page"
       "                        title-string        ; title"
       "                        (authors-list)      ; authors"
       "                        (extract-date d)    ; date"
       "                        (extract-version d) ; document version"
       "                        null                ; tags"
       "                        (unless (part-style? d 'no-toc+aux)"
       "                         (list-of-toc-view d ri)) ; tocset"
       "                        article-xexpr       ; article"
       "                        )]"
       "                      [article-string (open-output-string)])"
       "                  (begin"
       "                    (write article article-string)"
       "                    (displayln (program-format (get-output-string article-string) #:width 72))))"
       "                (xml:write-xexpr part-xexpr))))))")
  #:start-line 1165)
Syntactic replacement
(syntax-replacement
  #:introduction-scope #<procedure:...and/syntax-local.rkt:148:2>
  #:new-syntax
    #<syntax:/home/runner/.local/share/racket/8.17.0.1/pkgs/resyntax/default-recommendations/conditional-shortcuts.rkt:218:2 (cond (xexpr-out? (define article (scribble-xexpr-page title-string (authors-list) (extract-date d) (extract-version d) null (unless (part-style? d (quote no-toc+aux)) (list-of-toc-view d ri)) article-xexpr)) (define article-string (open-output-string))...>
  #:original-syntax
    #<syntax:scribble-lib/scribble/html-render.rkt:1165:12 (if xexpr-out? (let ((article (scribble-xexpr-page title-string (authors-list) (extract-date d) (extract-version d) null (unless (part-style? d (quote no-toc+aux)) (list-of-toc-view d ri)) article-xexpr)) (article-string (open-output-string))) (begin (w...>
  #:source
    (file-source
     #<path:/home/runner/work/scribble/scribble/scribble-lib/scribble/html-render.rkt>))

@stevebyan
Copy link
Author

Regarding the build failures, while I prefer human-readable output files, if you don’t want to add fmt as a build dependency, I can just remove it from Scribble and add it to a build pass in my blog makefile.

@samth
Copy link
Member

samth commented Apr 18, 2025

For human-readable sexp output, just use pretty-write from racket/pretty instead of using fmt.

@stevebyan
Copy link
Author

For human-readable sexp output, just use pretty-write from racket/pretty instead of using fmt.

Thanks, I’ll make that change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants