◊(Local Yarn Code "pollen.scrbl at [dc332aff]")

File code-docs/pollen.scrbl artifact ecc33834 part of check-in dc332aff


#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.