#lang scribble/manual
@; SPDX-License-Identifier: BlueOak-1.0.0
@; This file is licensed under the Blue Oak Model License 1.0.0.
@(require "scribble-helpers.rkt")
@(require (for-label "../pollen.rkt"
"../dust.rkt"
"../crystalize.rkt"
racket/base
racket/contract
racket/string
txexpr
pollen/tag
pollen/setup
pollen/core
sugar/coerce))
@title[#:tag "pollen-rkt"]{Pollen}
@defmodule["pollen.rkt" #:packages ()]
The file @filepath{pollen.rkt} is implicitly @code{require}d in every template and every @code{#lang
pollen} file in the project. It defines the markup for all Pollen documents, and also re-provides
everything provided by @filepath{cache.rkt} and @filepath{crystalize.rkt}.
The @code{setup} module towards the top of the file is used as described in
@racketmodname[pollen/setup].
@section{Markup reference}
These are the tags that can be used in any of @italic{The Local Yarn}’s Pollen documents (articles,
etc).
@defproc[(title [element xexpr?] ...) txexpr?]
@margin-note{The @code{title} function is not actually defined in @filepath{pollen.rkt} or anywhere
else. In Pollen, any undefined function @tt{title} defaults to @racket[(default-tag-function
title)], which is what I want. It is documented here because its presence or absence has
side-effects on the display of the article.}
Supplies a title for the document. You can use any otherwise-valid markup within the title tag.
Titles are optional; if you don’t specify a title, the article will appear without one. This is
a feature!
@defproc[(p [element xexpr?] ...) txexpr?]
Wrap text in a paragraph. You almost never need to use this tag explicitly;
just separate paragraphs by an empty line.
Single newlines within a paragraph will be replaced by spaces, allowing you to use
@ext-link["https://scott.mn/2014/02/21/semantic_linewrapping/"]{semantic line wrapping}.
@defproc[(newthought [element xexpr?] ...) txexpr?]
An inline style intended for the first few words of the first paragraph in a new section. Applies
a “small caps” style to the text. Any paragraph containing a @code{newthought} tag is given extra
vertical leading.
Rule of thumb: within an article, use either @code{section}/@code{subsection} or @code{newthought}
to separate sections of text, but not both. Even better, keep it consistent across articles within
a series.
If you just need small caps without affecting the paragraph, use @code{smallcaps}.
@deftogether[(@defproc[(section [element xexpr?] ...) txexpr?]
@defproc[(subsection [element xexpr?] ...) txexpr?])]
Create second- and third-level headings, respectively. This is counting the article's title as the
first-level header (even if the current article has no title).
@defproc[(block [element xexpr?] ...) txexpr?]
A container for content that should appear grouped together on larger displays. Intended for use in
Series pages, where the template is very minimal. You would want output from
@racket[listing<>-short/articles] to appear inside a @racket[block], but you would want output from
@racket[listing<>-full/articles] to appear outside it (since each article effectively supplies its own
block). Only relevant to HTML output.
@deftogether[(@defproc[(link [link-id stringish?] [link-text xexpr?]) txexpr?]
@defproc[(url [link-id stringish?] [url string?]) void?])]
All hyperlinks are specified reference-style. So, to link some text, use the @code{link} tag with
an identifier, which can be a string, symbol or number. Elsewhere in the text, use @code{url} with
the same identifier to specify the URL:
@codeblock|{
#lang pollen
If you need help, ◊link[1]{Google it}.
◊url[1]{https://google.com}
}|
The @code{url} tag for a given identifier may be placed anywhere in the document, even before it is
referenced. If you create a @code{link} for an identifier that has no corresponding @code{url},
a @code{"Missing reference: [link-id]"} message will be substituted for the URL. Conversely,
creating a @code{url} that is never referenced will produce no output and no warnings or errors.
@deftogether[(@defproc[(figure [image-file string?] [caption xexpr?] ...) txexpr?]
@defproc[(figure-@2x [image-file string?] [caption xexpr?] ...) txexpr?])]
Insert a block-level image. The @racket[_image-file] should be supplied as a filename only, with no
folder names. It is assumed that the image is located inside an @racket[images-folder] within the
same folder as the source document.
For web output, using @racket[figure-@2x] will produce an image hard-coded to display at half its
actual size, or the width of the text block, whichever is smaller.
@defproc[(image-link [image-file string?] [link-text xexpr?] ...) txexpr?]
Adds a hyperlink to @racket[_image-file], supplied as a filename only with no folder names. It is
assumed that the image is located inside an @racket[images-folder] within the same folder as the
source document.
@deftogether[(@defproc[(fn [fn-id stringish?]) txexpr?]
@defproc[(fndef [fn-id stringish?] [elements xexpr?] ...) txexpr?])]
As with hyperlinks, footnotes are specified reference-style. In the output, footnotes will be
numbered according to the order in which their identifiers are referenced in the source document.
Example:
@codeblock|{
#lang pollen
Shoeless Joe Jackson was one of the best players of all time◊fn[1].
◊fndef[1]{But he might have lost the 1919 World Series on purpose.}
}|
You can refer to a given footnote definition more than once.
The @code{fndef} for a given id may be placed anywhere in the source document, even before it is
referenced. If you create a @code{fn} reference without a corresponding @code{fndef},
a @code{"Missing footnote definition!"} message will be substituted for the footnote text.
Conversely, creating a @code{fndef} that is never referenced will produce no output, warning or
error.
@deftogether[(@defproc[(dialogue [elements xexpr?] ...) txexpr?]
@defproc[(say [interlocutor string?] [elements xexpr?] ...) txexpr?]
@defproc[(saylines [interlocutor string?] [elements xexpr?] ...) txexpr?])]
Use these tags together for transcripts of dialogue, chats, screenplays, interviews and so
forth. The @racket[saylines] tag is the same as @racket[say] except that within @racket[saylines],
linebreaks within paragraphs are preserved.
Example usage:
@codeblock|{
#lang pollen
◊dialogue{
◊say["Tavi"]{You also write fiction, or you used to. Do you still?}
◊say["Lorde"]{The thing is, when I write now, it comes out as songs.}
}
}|
@defproc[(index [#:key key string? ""] [elements xexpr?] ...) txexpr?]
Creates a bidirectional link between this spot in the document and an entry in the keyword index
under @racket[_key]. If @racket[_key] is not supplied, the string contents of @racket[_elements] are
used as the key.
The example below will create two index entries, one under the heading “compassion” and one under
the heading “cats”:
@codeblock|{
#lang pollen
“I have a theory which I suspect is rather immoral,” Smiley
went on, more lightly. “Each of us has only a quantum of
◊index{compassion}. That if we lavish our concern on every
stray ◊index[#:key "cats"]{cat} we never get to the centre of
things. What do you think of it?”
}|
@defproc[(note [#:date date-str non-empty-string?]
[#:author author string? ""]
[#:author-url author-url string? ""]
[#:disposition disp-str string? ""]) txexpr?]
Add a note to the “Further Notes” section of the article. Notes are like blog comments but are
more rare and powerful; see @wiki{Differences from blogs}.
The @code{#:date} attribute is required and must be of the form @tt{YYYY-MM-DD}.
The @code{#:author} and @code{#:author-url} attributes can be used to credit notes from other
people. If the @code{#:author} attribute is not supplied then the value of @code{default-authorname}
is used.
The @code{#:disposition} attribute is used for notes that update or alter the whole disposition of
the article. It must be a string of the form @racket[_mark _past-tense-verb], where @racket[_mark]
is a symbol suitable for use as a marker, such as * or †, and @racket[_past-tense-verb] is the word
you want used to describe the article’s current state. An article stating a metaphysical position
might later be marked “recanted”; a prophecy or prediction might be marked “fulfilled”.
@codeblock|{
#lang pollen
◊note[#:date "2019-02-19" #:disposition "✓ verified"]{I wasn’t sure, but now I am.}
}|
If more than one note contains a @code{disposition} attribute, the one from the most recent note is
the one used.
Some caveats (for now):
@itemlist[
@item{Avoid defining new footnotes using @code{fndef} inside a @code{note}; these footnotes will
be placed into the main footnote section of the article, which is probably not what you want.}
]
@defproc[(verse [#:title title string? ""] [#:italic? italic boolean? #f] [element xexpr?] ...)
txexpr?]
Typeset contents as poetry, with line breaks preserved and the block centered on the longest line.
To set the whole block in italic, use @code{#:italic? #t} — otherwise, use @code{i} within the
block.
@defproc[(blockquote [element xexpr?] ...) txexpr?]
Surrounds a block quotation. To cite a source, include a @code{footer} tag at the bottom.
@defproc[(blockcode [element xexpr?] ...) txexpr?]
Typeset contents as a block of code using a monospace font. Line breaks are preserved.
@deftogether[(@defproc[(i [element xexpr?] ...) txexpr?]
@defproc[(em [element xexpr?] ...) txexpr?]
@defproc[(b [element xexpr?] ...) txexpr?]
@defproc[(strong [element xexpr?] ...) txexpr?]
@defproc[(strike [element xexpr?] ...) txexpr?]
@defproc[(ol [element xexpr?] ...) txexpr?]
@defproc[(ul [element xexpr?] ...) txexpr?]
@defproc[(item [element xexpr?] ...) txexpr?]
@defproc[(sup [element xexpr?] ...) txexpr?]
@defproc[(caps [element xexpr?] ...) txexpr?]
@defproc[(code [element xexpr?] ...) txexpr?])]
Work pretty much how you’d expect.
@section{Convenience macros}
@defform[(for/s thing-id listofthings result-exprs ...)
#:contracts ([listofthings (listof any/c)])]
A shorthand form for Pollen’s @code{for/splice} that uses far fewer brackets when you’re only
iterating through a single list.
@codeblock|{
#lang pollen
◊for/s[x '(7 8 9)]{Now once for number ◊x}
◊;Above line is shorthand for this one:
◊for/splice[[(x (in-list '(7 8 9)))]]{Now once for number ◊x}
}|
@section{Defining new tags}
I use a couple of macros to define tag functions that automatically branch into other functions
depending on the current output target format. This allows me to put the format-specific tag
functions in separate files that have separate places in the dependency chain. So if only the HTML
tag functions have changed and not those for PDF, the @filepath{makefile} can ensure only the HTML files are
rebuilt.
@defproc[#:kind "syntax"
(poly-branch-tag (tag-id symbol?))
(-> txexpr?)]
Defines a new function @racket[_tag-id] which will automatically pass all of its arguments to a
function whose name is the value returned by @racket[current-poly-target], followed by a hyphen,
followed by @racket[_tag]. So whenever the current output format is @racket['html], the function
defined by @racket[(poly-branch-tag _p)] will branch to a function named @racket[html-p]; when the
current format is @racket['pdf], it will branch to @racket[pdf-p], and so forth.
You @emph{must} define these branch functions separately, and you must define one for @emph{every}
output format included in the definition of @racket[poly-targets] in this file’s @racket[setup]
submodule. If you do not, you will get “unbound identifier” errors at expansion time.
The convention in this project is to define and provide these branch functions in separate files:
see, e.g., @filepath{tags-html.rkt}.
Functions defined with this macro @emph{do not} accept keyword arguments. If you need keyword
arguments, see @racket[poly-branch-kwargs-tag].
@margin-note{The thought behind having two macros so similar is that, by cutting out handling for keyword
arguments, @racket[poly-branch-tag] could produce simpler and faster code. I have not verified if
this intuition is meaningful or correct.}
@defproc[#:kind "syntax"
(poly-branch-kwargs-tag (tag-id symbol?))
(-> txexpr?)]
Works just like @racket[poly-branch-tag], but uses Pollen’s @racket[define-tag-function] so that
keyword arguments will automatically be parsed as X-expression attributes.
Additionally, the branch functions called from the new function must accept exactly two arguments:
a list of attributes and a list of elements.