(define (hanging-topic topic-xexpr . tx-elements)
@@ -660,59 +662,34 @@ Convert a topic + subhead into one HTML markup unit
(quick-table
[table-rows xexpr?] ...)
txexpr?]
-Make an HTML table using simplified notation
-
-◊quick-table{heading left | heading center | heading right
-upper left | upper center | upper right
-lower left | lower center | lower right}
+Make an HTML table using simplified notation. In HTML, wrapping every paragraph in tags is a terrible and dull task. But formatting tables is even worse. This function lets you make simple tables using @litchar{|} to signify columns, and line breaks to signify rows.
-In HTML, wrapping every paragraph in
tags is a terrible and dull task.
-But formatting tables is even worse.
-
-This function lets you make simple tables using "|" to signify columns,
-and line breaks to signify rows.
-
-Let's uncork a few more whizzy Racket commands while we're at it.
+@terminal{
+ ◊quick-table{
+ heading left | heading center | heading right
+ upper left | upper center | upper right
+ lower left | lower center | lower right}}
-This function assumes that each row has the same number of columns.
-You could improve it to fill in blank cells in rows that need them.
+This function assumes that each row has the same number of columns. You could improve it to fill in blank cells in rows that need them.
+The idea is to break down the input into table headings and cells, and then work back up, wrapping each layer in the appropriate tags.
@chunk[
(define (quick-table . tx-elements)
-
- ;; In Pollen, a multiline tx-elements block arrives as a list of lines and linebreak characters.
- ;; (A situation we already encountered in @racket[detect-list-items].)
(define rows-of-text-cells
- (let ([text-rows (filter-not whitespace? tx-elements)]) ; throw out the linebreak characters
- ;; @racket[for/list] is very handy: a @racket[for] loop that gathers the results into a list.
- ;; Think of it as a more flexible version of @racket[map].
+ (let ([text-rows (filter-not whitespace? tx-elements)])
(for/list ([text-row (in-list text-rows)])
- ;; the cells are delimited within a row by "|", so split on this char
(for/list ([text-cell (in-list (string-split text-row "|"))])
- (string-trim text-cell))))) ; trim remaining whitespace from cell text
+ (string-trim text-cell)))))
- ;; Racket's @racket[match] functions are very useful.
- ;; Among other things, they can be used for Python-style data unpacking.
- ;; The expression on the right will produce three tag functions;
- ;; the @racket[match-define] assigns them to three new identifiers.
(match-define (list tr-tag td-tag th-tag) (map make-default-tag-function '(tr td th)))
- ;; now we'll take our rows of text cells and apply cell-level HTML tags.
- ;; the first row will get 'th tags; the other rows get 'td tags.
(define html-rows
- ;; another use of @racket[match]. Notice how this @racket[cons] is used to separate a list into parts ...
(match-let ([(cons header-row other-rows) rows-of-text-cells])
- ;; ... whereas this @racket[cons] is used to combine parts into a list
(cons (map th-tag header-row)
(for/list ([row (in-list other-rows)])
(map td-tag row)))))
- ;; With the cells tagged up, add the row tags and finally the table tag.
- ;; Notice that we use @racket[apply] with @racket[tr-tag] to unpack the list of cells in each html-row.
- ;; Remember that @racket[apply] does something very simple:
- ;; Converts an expression of the form @racket[(apply func (list arg1 arg2 ...))]
- ;; Into @racket[(func arg1 arg2 ...)]
(cons 'table (for/list ([html-row (in-list html-rows)])
(apply tr-tag html-row))))]
@@ -722,27 +699,22 @@ You could improve it to fill in blank cells in rows that need them.
txexpr?]
Create a thumbnail of a PDF that links to the PDF.
-This function will only work properly if you have @racket[sips] on your system
-(command-line image-processing program, included with OS X).
+This function will only work properly if you have @tt{sips} on your system (= command-line image-processing program, included with OS X).
-This shows how you can fold other kinds of project housekeeping into Pollen commands.
-Here, the function generates the thumbnail it needs when the page is compiled.
+This shows how you can fold other kinds of project housekeeping into Pollen commands. Here, the function generates the thumbnail it needs when the page is compiled.
-One disadvantage of this approach is that the thumbnail will *always* be generated on recompile,
-though you could put in some logic to avoid this (e.g., check the modification date of the PDF).
-In this case, @racket[sips] is fast enough that it's not bothersome.
+One disadvantage of this approach is that the thumbnail will always be generated on recompile, though you could put in some logic to avoid this (e.g., check the modification date of the PDF). In this case, @tt{sips} is fast enough that it's not bothersome.
@chunk[
(define (pdf-thumbnail-link pdf-pathstring)
(define img-extension "gif")
- (define img-pathstring (->string (add-ext (remove-ext pdf-pathstring) img-extension)))
+ (define img-pathstring
+ (->string (add-ext (remove-ext pdf-pathstring) img-extension)))
(define sips-command
(format "sips -Z 2000 -s format ~a --out '~a' '~a' > /dev/null"
img-extension img-pathstring pdf-pathstring))
(link pdf-pathstring (if (system sips-command)
`(img ((src ,img-pathstring)))
- ;; usually one would raise an error on the next line,
- ;; but for instructional purposes, we'll have a graceful fail
"sips not available")))]
@deftogether[(
@@ -759,53 +731,55 @@ In this case, @racket[sips] is fast enough that it's not bothersome.
[base-name string?])
txexpr?]
)]
-A few convenience variants of @racket[pdf-thumbnail-link]
+A few convenience variants of @racket[pdf-thumbnail-link].
@chunk[
(define (pdf-thumbnail-link-from-metas metas)
- (define-values (dir fn _) (split-path (add-ext (remove-ext* (hash-ref metas 'here-path)) "pdf")))
+ (define-values (dir fn _)
+ (split-path (add-ext (remove-ext* (hash-ref metas 'here-path)) "pdf")))
(pdf-thumbnail-link (->string fn)))
(define (before-and-after-pdfs base-name)
`(div
(div ((class "pdf-thumbnail"))
"before" (br)
- ,(pdf-thumbnail-link (format "pdf/sample-doc-~a-before.pdf" base-name)))
+ ,(pdf-thumbnail-link
+ (format "pdf/sample-doc-~a-before.pdf" base-name)))
(div ((class "pdf-thumbnail"))
"after" (br)
- ,(pdf-thumbnail-link (format "pdf/sample-doc-~a-after.pdf" base-name)))))
+ ,(pdf-thumbnail-link
+ (format "pdf/sample-doc-~a-after.pdf" base-name)))))
(define (alternate-after-pdf base-name)
`(div ((class "pdf-thumbnail"))
- "after (alternate)" (br)
- ,(pdf-thumbnail-link (format "pdf/sample-doc-~a-after-alternate.pdf" base-name))))]
+ "after (alternate)"
+ (br)
+ ,(pdf-thumbnail-link
+ (format "pdf/sample-doc-~a-after-alternate.pdf" base-name))))]
@defproc[
(root
- [tx-elements (listof txexpr?)] ...)
+ [tx-element txexpr?] ...)
txexpr?]
-Decode page content
+Decode page content.
+
+In a @seclink["Writing_with_Pollen_markup"
+ #:doc '(lib "pollen/scribblings/pollen.scrbl")]{Pollen markup} source, the output is a tagged X-expression that starts with @racket[root]:
-In a Pollen markup source, the output is a tagged X-expression that starts with @racket[root]:
+@terminal{(root (div ((class "headline")) "Page title") ...)}
-(root (div ((class "headline")) "Page title") ...)
+Recall that every Pollen tag calls a function with the same name (if it exists, otherwise it just becomes a tag). This is also true of @racket[root].
-Recall that every Pollen tag calls a function with the same name (if it exists, otherwise it just
-becomes a tag). This is also true of @racket[root].
+@racket[root] has slightly special status inasmuch as it is the top tag of the X-expression, and thus the last tag function that will get called. Therefore, @racket[root] is a good place to put any processing that should happen once all the page content has been filled in.
-@racket[root] has slightly special status inasmuch as it is the top tag of the X-expression,
-and thus the last tag function that will get called. Therefore, @racket[root] is a good place to put any
-processing that should happen once all the page content has been filled in.
+Often, you'll want to use a @racket[decode] or @racket[decode-elements] function, which can recursively perform different kinds of processing on different types of page elements.
-Often, you'll want to use a @racket[decode] function, which can recursively perform different kinds of
-processing on different types of page elements.
+In this case, we'll use @racket[decode-elements] twice. First, we'll use it just to detect paragraphs. We'll do this so that they're treated as @seclink["Block" #:doc '(lib "pollen/scribblings/pollen.scrbl")]{blocks} in the second phase, which does the rest of the processing.
@chunk[
(define (root . elems)
- ;; We will do the decoding in two steps.
- ;; Detect paragraphs first so that they're treated as block-txexprs in next phase.
- (define elements-with-paragraphs (decode-elements elems #:txexpr-elements-proc detect-paragraphs))
- ;; Then do the rest of the decoding normally.
+ (define elements-with-paragraphs
+ (decode-elements elems #:txexpr-elements-proc detect-paragraphs))
(list* 'div '((id "doc"))
(decode-elements elements-with-paragraphs
#:block-txexpr-proc hyphenate-block
@@ -818,14 +792,12 @@ processing on different types of page elements.
(hyphenate-block
[block-tx txexpr?])
txexpr?]
-Helper function for root decoder
+Helper function for @racket[root] decoder that handles hyphenation.
+
+The basic @racket[hyphenate] function comes from the @racketmodname[hyphenate] module. We could attach @racket[hyphenate] to our @racket[root] decoder as a string processor rather than block processor. But we want to be able to handle our no-hyphens flag (aka @racket[no-hyphens-attr]), which is stored in the attributes of the X-expression. Therefore, we have to look at blocks, not just strings.
@chunk[
(define (hyphenate-block block-tx)
- ;; The basic @racket[hyphenate] function comes from the @racket[hyphenate] module.
- ;; We could attach @racket[hyphenate] to our decoder as a string processor rather than block processor.
- ;; But we want to be able to handle our "no-hyphens" flag (aka @racket[no-hyphens-attr]).
- ;; So we want to look at blocks, not strings.
(define (no-hyphens? tx)
(or (member (get-tag tx) '(th h1 h2 h3 h4 style script)) ; don't hyphenate these, no matter what
(member no-hyphens-attr (get-attrs tx)))) ; also don't hyphenate blocks with @racket[no-hyphens-attr]
@@ -840,15 +812,10 @@ Helper function for root decoder
txexpr?]
Perform tricky processing on quotation marks.
-Because I'm a typography snob I like to push quotation marks into the margin a little bit
-when they appear at the left edge of a line (aka "hanging quotes").
-
-This function just wraps left-hand quote marks in two little tags ("push" and "pull")
-that I can then manipulate in CSS to get the effect.
+Because I'm a typography snob I like to push quotation marks into the margin a little bit when they appear at the left edge of a line (aka hanging quotes). This function just wraps left-hand quote marks in two little tags (@tt{push} and @tt{pull}) that I can then manipulate in CSS to get the effect.
@chunk[
(define (make-quotes-hangable str)
- ;; using @racket[regexp-match*] with #:gap-select? makes it act like a funny kind of string splitter
(define substrs (regexp-match* #px"\\s?[“‘]" str #:gap-select? #t))
(if (= (length substrs) 1) ; no submatches
(car substrs)
@@ -865,14 +832,12 @@ that I can then manipulate in CSS to get the effect.
(fix-em-dashes
[str string?])
txexpr?]
-Helper function for root decoder
+Helper function for @racket[root] decoder.
-When I type an em dash in my sources, I will often leave a space around it,
-but I don't want spaces in the output, so this function removes them.
+When I type an em dash in my sources, I will often leave a space around it, but I don't want spaces in the output, so this function removes them.
@chunk[
(define (fix-em-dashes str)
- ;; \u00A0 = nbsp, \u2009 = thinsp (neither included in \s)
(let* ([str (regexp-replace* #px"(?<=\\w)[\u00A0\u2009\\s]—" str "—")]
[str (regexp-replace* #px"—[\u00A0\u2009\\s](?=\\w)" str "—")])
str))]
@@ -955,7 +920,7 @@ For use in our HTML templates. We could also define this function inside a templ
@section{Finally}
-This last incantation is needed so @racketmodname[scribble/lp2] knows how to put together all the code chunks we've introduced in this file.
+This last incantation is needed so this @racketmodname[scribble/lp2] document knows how to put together all the code chunks we've introduced in this file.
@chunk[<*>