improve markdown renderer, including support for section links

This commit is contained in:
Matthew Flatt 2019-10-15 19:01:40 -06:00
parent e44a5bb9f1
commit 431bb95c7b
5 changed files with 120 additions and 31 deletions

View File

@ -2201,7 +2201,7 @@ For HTML rendering:
@filepath{manual-style.css} from the @filepath{scribble} @filepath{manual-style.css} from the @filepath{scribble}
collection in @racket[html-defaults].} collection in @racket[html-defaults].}
@item{The file @filepath{manual-files.css} from the @item{The file @filepath{manual-fonts.css} from the
@filepath{scribble} collection is designated as an additional @filepath{scribble} collection is designated as an additional
accompanying file in @racket[html-defaults].} accompanying file in @racket[html-defaults].}

View File

@ -607,6 +607,13 @@ Code blocks are marked using the
"Github convention"] @verbatim{```racket} so that they are lexed and "Github convention"] @verbatim{```racket} so that they are lexed and
formatted as Racket code.}} formatted as Racket code.}}
@defboolparam[current-markdown-link-sections enabled?]{
Determines whether section links within an output document are
rendered as a section link. The default is @racket[#f].
@history[#:added "1.31"]}
@; ---------------------------------------- @; ----------------------------------------
@section{HTML Renderer} @section{HTML Renderer}

View File

@ -23,4 +23,4 @@
(define pkg-authors '(mflatt eli)) (define pkg-authors '(mflatt eli))
(define version "1.30") (define version "1.31")

View File

@ -4,7 +4,10 @@
"private/render-utils.rkt" "private/render-utils.rkt"
racket/class racket/port racket/list racket/string racket/match racket/class racket/port racket/list racket/string racket/match
scribble/text/wrap) scribble/text/wrap)
(provide render-mixin) (provide render-mixin
current-markdown-link-sections)
(define current-markdown-link-sections (make-parameter #f))
(define current-indent (make-parameter 0)) (define current-indent (make-parameter 0))
(define (make-indent amt) (define (make-indent amt)
@ -17,6 +20,9 @@
(indent)) (indent))
(define note-depth (make-parameter 0)) (define note-depth (make-parameter 0))
(define in-toc (make-parameter #f))
(define markdown-part-tag 'markdown-section)
(define (render-mixin %) (define (render-mixin %)
(class % (class %
@ -37,6 +43,16 @@
format-number format-number
number-depth) number-depth)
(define/override (collect-part-tags d ci number)
(for ([t (part-tags d)])
(let ([t (generate-tag t ci)])
(collect-put! ci
t
(vector (or (part-title-content d) '("???"))
(add-current-tag-prefix t)
number
markdown-part-tag)))))
(define/override (render-part d ht) (define/override (render-part d ht)
(let ([number (collected-info-number (part-collected-info d ht))]) (let ([number (collected-info-number (part-collected-info d ht))])
(unless (part-style? d 'hidden) (unless (part-style? d 'hidden)
@ -153,6 +169,13 @@
(write-string (make-string (note-depth) #\>)) (write-string (make-string (note-depth) #\>))
(unless (zero? (note-depth)) (unless (zero? (note-depth))
(write-string " "))) (write-string " ")))
(cond
[(in-toc)
(write-note)
(super render-paragraph p part ri)
;; two spaces at a line end creates a line break:
(write-string " ")]
[else
(define o (open-output-string)) (define o (open-output-string))
(parameterize ([current-output-port o]) (parameterize ([current-output-port o])
(super render-paragraph p part ri)) (super render-paragraph p part ri))
@ -171,7 +194,7 @@
(write-note) (write-note)
(write-string (car lines)) (write-string (car lines))
(for ([line (in-list (cdr lines))]) (for ([line (in-list (cdr lines))])
(newline) (indent) (write-note) (write-string line)) (newline) (indent) (write-note) (write-string line))])
(newline) (newline)
null) null)
@ -230,10 +253,10 @@
[(and (code? i) (not (in-code?))) [(and (code? i) (not (in-code?)))
(recurse-wrapped "`" in-code?)] (recurse-wrapped "`" in-code?)]
[(and (bold? i) (not (in-bold?))) [(and (bold? i) (not (in-bold?)) (not (in-code?)))
(recurse-wrapped "**" in-bold?)] (recurse-wrapped "**" in-bold?)]
[(and (italic? i) (not (in-italic?))) [(and (italic? i) (not (in-italic?)) (not (in-code?)))
(recurse-wrapped "_" in-italic?)] (recurse-wrapped "_" in-italic?)]
[(and (preserve-spaces? i) (not (preserving-spaces?))) [(and (preserve-spaces? i) (not (preserving-spaces?)))
@ -248,15 +271,45 @@
(render-content i part ri)) (render-content i part ri))
(printf "](~a)" (sanitize-parens link))))] (printf "](~a)" (sanitize-parens link))))]
[(and (link-element? i)
(current-markdown-link-sections)
(not (in-link?))
;; Link to a part within this document?
(let ([vec (resolve-get part ri (link-element-tag i))])
(and (vector? vec)
(= 4 (vector-length vec))
(eq? markdown-part-tag (vector-ref vec 3))
vec)))
=> (lambda (vec)
(define s (string-append
(let ([s (if (vector-ref vec 2)
(format-number (vector-ref vec 2) '() #t)
'())])
(if (null? s)
""
(string-append (car s) " ")))
(content->string (vector-ref vec 0))))
(display "[")
(begin0
(parameterize ([in-link? #t])
(super render-content i part ri))
(display "](#")
(display (regexp-replace* #" "
(regexp-replace* #rx"[^a-zA-Z0-9_ -]" (string-downcase s) "")
#"-"))
(display ")")))]
[else (super render-content i part ri)])) [else (super render-content i part ri)]))
(define/override (render-nested-flow i part ri starting-item?) (define/override (render-nested-flow i part ri starting-item?)
(define s (nested-flow-style i)) (define s (nested-flow-style i))
(unless (memq 'decorative (style-properties s)) (unless (memq 'decorative (style-properties s))
(define note? (equal? (style-name s) "refcontent")) (define note? (equal? (style-name s) "refcontent"))
(define toc? (equal? (style-name s) 'table-of-contents))
(when note? (when note?
(note-depth (add1 (note-depth)))) (note-depth (add1 (note-depth))))
(begin0 (super render-nested-flow i part ri starting-item?) (begin0 (parameterize ([in-toc (or toc? (in-toc))])
(super render-nested-flow i part ri starting-item?))
(when note? (when note?
(note-depth (sub1 (note-depth))))))) (note-depth (sub1 (note-depth)))))))
@ -270,8 +323,8 @@
[(rdquo) "\U201D"] [(rdquo) "\U201D"]
[(lsquo) "\U2018"] [(lsquo) "\U2018"]
[(rsquo) "\U2019"] [(rsquo) "\U2019"]
[(lang) ">"] [(lang) "<"]
[(rang) "<"] [(rang) ">"]
[(rarr) "->"] [(rarr) "->"]
[(nbsp) "\uA0"] [(nbsp) "\uA0"]
[(prime) "'"] [(prime) "'"]
@ -280,10 +333,17 @@
[else (error 'markdown-render "unknown element symbol: ~e" [else (error 'markdown-render "unknown element symbol: ~e"
i)]))] i)]))]
[(string? i) [(string? i)
(let* ([i (if (in-code?) (let* ([i (cond
[(in-code?)
(regexp-replace** i '([#rx"``" . "\U201C"] (regexp-replace** i '([#rx"``" . "\U201C"]
[#rx"''" . "\U201D"])) [#rx"''" . "\U201D"]))]
(regexp-replace* #px"([#_*`\\[\\(\\]\\)]{1})" i "\\\\\\1"))] [(or (in-link?)
(regexp-match? #rx"^[(]" i)
(regexp-match? #rx"[]][(]" i))
(regexp-replace* #px"([#_*`\\[\\(\\]\\)]{1})" i "\\\\\\1")]
[else
;; Avoid escaping parentheses
(regexp-replace* #px"([#_*`\\[\\]]{1})" i "\\\\\\1")])]
[i (if (preserving-spaces?) [i (if (preserving-spaces?)
(regexp-replace* #rx" " i "\uA0") (regexp-replace* #rx" " i "\uA0")
i)]) i)])
@ -291,6 +351,26 @@
[else (write i)]) [else (write i)])
null) null)
(define/override (table-of-contents part ri)
(define t (super table-of-contents part ri))
(cond
[(current-markdown-link-sections)
;; Table generated by `table-of-contents` always has one
;; column, and each row has one paragraph that starts
;; with a 'hspace element to indent
(nested-flow
(style 'table-of-contents null)
(for/list ([p (map car (table-blockss t))])
(define c (paragraph-content p))
(define keep-c (cdr c))
(define (spaces->depth n)
(add1 (quotient (- n 4) 2)))
(for/fold ([p (paragraph plain keep-c)]) ([s (in-range
(spaces->depth
(string-length (car (element-content (car c))))))])
(nested-flow (style "refcontent" null) (list p)))))]
[else t]))
(super-new))) (super-new)))
(define (regexp-replace** str ptns&reps) (define (regexp-replace** str ptns&reps)

View File

@ -106,6 +106,8 @@
(current-style-file file)] (current-style-file file)]
[("--prefix") file "use given .html/.tex prefix (for doctype/documentclass)" [("--prefix") file "use given .html/.tex prefix (for doctype/documentclass)"
(current-prefix-file file)] (current-prefix-file file)]
[("--link-section") "support section links for markdown"
(markdown:current-markdown-link-sections #t)]
#:multi #:multi
[("++extra") file "add given file" [("++extra") file "add given file"
(current-extra-files (cons file (current-extra-files)))] (current-extra-files (cons file (current-extra-files)))]