Module reference
1 Cache
current-cache
cached-require
make-cache
reset-cache
cache-ref
2 Decode
decode
2.1 Block
project-block-tags
register-block-tag
block-txexpr?
2.2 Typography
whitespace?
whitespace/  nbsp?
smart-quotes
smart-dashes
detect-linebreaks
detect-paragraphs
3 File
preproc-source?
markup-source?
template-source?
null-source?
scribble-source?
pagetree-source?
has-preproc-source?
has-markup-source?
has-template-source?
has-null-source?
has-scribble-source?
has/  is-preproc-source?
has/  is-markup-source?
has/  is-template-source?
has/  is-null-source?
has/  is-scribble-source?
->preproc-source-path
->markup-source-path
->template-source-path
->null-source-path
->scribble-source-path
->output-path
4 Pagetree
pagetree?
validate-pagetree
pagenode?
pagenodeish?
->pagenode
4.1 Navigation
current-pagetree
parent
children
siblings
previous
previous*
next
next*
4.2 Utilities
pagetree->list
in-pagetree?
path->pagenode
5 Render
render
render-to-file
render-to-file-if-needed
render-batch
render-pagetree
get-template-for
6 Template
->html
select
select*
select-from-metas
select-from-doc
7 Tag
make-tag-function
8 Top
#%top
def/  c
9 World
world:  main-pollen-export
world:  meta-pollen-export
world:  project-require
world:  check-project-requires-in-render?
world:  server-extras-dir
world:  current-server-extras-path
world:  preproc-source-ext
world:  markup-source-ext
world:  markdown-source-ext
world:  null-source-ext
world:  pagetree-source-ext
world:  template-source-ext
world:  scribble-source-ext
world:  decodable-extensions
world:  mode-auto
world:  mode-preproc
world:  mode-markup
world:  mode-markdown
world:  mode-pagetree
world:  default-pagetree
world:  pagetree-root-node
world:  command-marker
world:  default-template-prefix
world:  fallback-template
world:  template-meta-key
world:  newline
world:  linebreak-separator
world:  paragraph-separator
world:  dashboard-css
world:  paths-excluded-from-dashboard
6.0.0.5

Module reference

    1 Cache

    2 Decode

      2.1 Block

      2.2 Typography

    3 File

    4 Pagetree

      4.1 Navigation

      4.2 Utilities

    5 Render

    6 Template

    7 Tag

    8 Top

    9 World

1 Cache

 (require pollen/cache) package: pollen

The slowest part of a render is parsing and decoding the source file. Often, previewing a single source file necessarily means decoding others (for instance templates, or other source files that are linked into the main source file). But usually, only one source file is changing at a time. Therefore, Pollen stores copies of the exports of source files — namely, whatever is stored in doc and metas — in the cache so they can be reused.

parameter

(current-cache)  hash?

(current-cache hash)  void?
  hash : hash?
 = (make-cache)
A parameter that refers to the current cache. It is initialized with make-cache.

The cache is a hash table that uses the complete path of a source file as its keys. The value associated with each of these keys is a subcache — another hash table with keys 'doc, 'metas (for storing the exports of the source file) and 'mod-time (for storing the modification time, provided by file-or-directory-modify-seconds).

procedure

(cached-require source-path key)  (or/c txexpr? hash? integer?)

  source-path : pathish?
  key : (or/c 'doc 'metas 'mod-time)
Similar to (dynamic-require source-path key), except that it first tries to retrieve the requested value out of current-cache. If it’s not there, or out of date, dynamic-require is used to update the value.

The only keys supported are 'doc, 'metas, and 'mod-time.

If you want the speed benefit of the cache, you should always use cached-require to get data from Pollen source files. That doesn’t mean you can’t still use functions like require, local-require, and dynamic-require. They’ll just be slower.

procedure

(make-cache)  hash?

Initializes current-cache.

procedure

(reset-cache)  void?

Clears current-cache. When only the nuclear option will do.

procedure

(cache-ref source-path)  hash?

  source-path : pathish?
Returns the subcache associated with the key source-path, which will itself be a hash table. See current-cache.

2 Decode

 (require pollen/decode) package: pollen

The doc export of a Pollen markup file is a simple X-expression. Decoding refers to any post-processing of this X-expression. The pollen/decode module provides tools for creating decoders.

The decode step can happen separately from the compilation of the file. But you can also attach a decoder to the markup file’s root node, so the decoding happens automatically when the markup is compiled, and thus automatically incorporated into doc. (Following this approach, you could also attach multiple decoders to different tags within doc.)

You can, of course, embed function calls within Pollen markup. But since markup is optimized for authors, decoding is useful for operations that can or should be moved out of the authoring layer.

One example is presentation and layout. For instance, detect-paragraphs is a decoder function that lets authors mark paragraphs in their source simply by using two carriage returns.

Another example is conversion of output into a particular data format. Most Pollen functions are optimized for HTML output, but one could write a decoder that targets another format.

procedure

(decode tagged-xexpr    
  [#:txexpr-tag-proc txexpr-tag-proc    
  #:txexpr-attrs-proc txexpr-attrs-proc    
  #:txexpr-elements-proc txexpr-elements-proc    
  #:block-txexpr-proc block-txexpr-proc    
  #:inline-txexpr-proc inline-txexpr-proc    
  #:string-proc string-proc    
  #:symbol-proc symbol-proc    
  #:valid-char-proc valid-char-proc    
  #:cdata-proc cdata-proc    
  #:exclude-tags tags-to-exclude])  txexpr?
  tagged-xexpr : txexpr?
  txexpr-tag-proc : (txexpr-tag? . -> . txexpr-tag?)
   = (λ(tag) tag)
  txexpr-attrs-proc : (txexpr-attrs? . -> . txexpr-attrs?)
   = (λ(attrs) attrs)
  txexpr-elements-proc : (txexpr-elements? . -> . txexpr-elements?)
   = (λ(elements) elements)
  block-txexpr-proc : (block-txexpr? . -> . xexpr?) = (λ(tx) tx)
  inline-txexpr-proc : (txexpr? . -> . xexpr?) = (λ(tx) tx)
  string-proc : (string? . -> . xexpr?) = (λ(str) str)
  symbol-proc : (symbol? . -> . xexpr?) = (λ(sym) sym)
  valid-char-proc : (valid-char? . -> . xexpr?) = (λ(vc) vc)
  cdata-proc : (cdata? . -> . xexpr?) = (λ(cdata) cdata)
  tags-to-exclude : (listof symbol?) = null
Recursively process a tagged-xexpr, usually the one exported from a Pollen source file as doc.

This function doesn’t do much on its own. Rather, it provides the hooks upon which harder-working functions can be hung.

Recall from (part "Pollen mechanics") that any tag can have a function attached to it. By default, the tagged-xexpr from a source file is tagged with root. So the typical way to use decode is to attach your decoding functions to it, and then define root to invoke your decode function. Then it will be automatically applied to every doc during compile.

For instance, here’s how decode is attached to root in Butterick’s Practical Typography. There’s not much to it —

(define (root . items)
    (decode (make-txexpr 'root null items)
        #:txexpr-elements-proc detect-paragraphs
        #:block-txexpr-proc
            (λ(bx) (wrap-hanging-quotes (nonbreaking-last-space bx)))
        #:string-proc (compose1 smart-quotes smart-dashes)))

This illustrates another important point: even though decode presents an imposing list of arguments, you’re unlikely to use all of them at once. These represent possibilities, not requirements. For instance, let’s see what happens when decode is invoked without any of its optional arguments.

Examples:

> (define tx '(root "I wonder" (em "why") "this works."))
> (decode tx)

'(root "I wonder" (em "why") "this works.")

Right — nothing. That’s because the default value for the decoding arguments is the identity function, (λ (x) x). So all the input gets passed through intact unless another action is specified.

The *-proc arguments of decode take procedures that are applied to specific categories of elements within txexpr.

The txexpr-tag-proc argument is a procedure that handles X-expression tags.

Examples:

> (define tx '(p "I'm from a strange" (strong "namespace")))
; Tags are symbols, so a tag-proc should return a symbol
> (decode tx #:txexpr-tag-proc (λ(t) (string->symbol (format "ns:~a" t))))

'(ns:p "I'm from a strange" (ns:strong "namespace"))

The txexpr-attrs-proc argument is a procedure that handles lists of X-expression attributes. (The txexpr module, included at no extra charge with Pollen, includes useful helper functions for dealing with these attribute lists.)

Examples:

> (define tx '(p [[id "first"]] "If I only had a brain."))
; Attrs is a list, so cons is OK for simple cases
> (decode tx #:txexpr-attrs-proc (λ(attrs) (cons '[class "PhD"] attrs)))

'(p ((class "PhD") (id "first")) "If I only had a brain.")

Note that txexpr-attrs-proc will change the attributes of every tagged X-expression, even those that don’t have attributes. This is useful, because sometimes you want to add attributes where none existed before. But be careful, because the behavior may make your processing function overinclusive.

Examples:

> (define tx '(div (p [[id "first"]] "If I only had a brain.")
  (p "Me too.")))
; This will insert the new attribute everywhere
> (decode tx #:txexpr-attrs-proc (λ(attrs) (cons '[class "PhD"] attrs)))

'(div

  ((class "PhD"))

  (p ((class "PhD") (id "first")) "If I only had a brain.")

  (p ((class "PhD")) "Me too."))

; This will add the new attribute only to non-null attribute lists
> (decode tx #:txexpr-attrs-proc
  (λ(attrs) (if (null? attrs) attrs (cons '[class "PhD"] attrs))))

'(div (p ((class "PhD") (id "first")) "If I only had a brain.") (p "Me too."))

The txexpr-elements-proc argument is a procedure that operates on the list of elements that represents the content of each tagged X-expression. Note that each element of an X-expression is subject to two passes through the decoder: once now, as a member of the list of elements, and also later, through its type-specific decoder (i.e., string-proc, symbol-proc, and so on).

Examples:

> (define tx '(div "Double" "\n" "toil" amp "trouble"))
; Every element gets doubled ...
> (decode tx #:txexpr-elements-proc (λ(es) (append-map (λ(e) `(,e ,e)) es)))

'(div "Double" "Double" "\n" "\n" "toil" "toil" amp amp "trouble" "trouble")

; ... but only strings get capitalized
> (decode tx #:txexpr-elements-proc (λ(es) (append-map (λ(e) `(,e ,e)) es))
  #:string-proc (λ(s) (string-upcase s)))

'(div "DOUBLE" "DOUBLE" "\n" "\n" "TOIL" "TOIL" amp amp "TROUBLE" "TROUBLE")

So why do you need txexpr-elements-proc? Because some types of element decoding depend on context, thus it’s necessary to handle the elements as a group. For instance, the doubling function above, though useless, requires handling the element list as a whole, because elements are being added.

A more useful example: paragraph detection. The behavior is not merely a map across each element:

Examples:

> (define (paras tx) (decode tx #:txexpr-elements-proc detect-paragraphs))
; Context matters. Trailing whitespace is ignored ...
> (paras '(body "The first paragraph." "\n\n"))

'(body "The first paragraph.")

; ... but whitespace between strings is converted to a break.
> (paras '(body "The first paragraph." "\n\n" "And another."))

'(body (p "The first paragraph.") (p "And another."))

; A combination of both types
> (paras '(body "The first paragraph." "\n\n" "And another." "\n\n"))

'(body (p "The first paragraph.") (p "And another."))

The block-txexpr-proc argument and the inline-txexpr-proc arguments are procedures that operate on tagged X-expressions. If the X-expression meets the block-txexpr? test, it is processed by block-txexpr-proc. Otherwise, it is processed by inline-txexpr-proc. Thus every tagged X-expression will be handled by one or the other. Of course, if you want block and inline elements to be handled the same way, you can set block-txexpr-proc and inline-txexpr-proc to be the same procedure.

Examples:

> (define tx '(div "Please" (em "mind the gap") (h1 "Tuesdays only")))
> (define add-ns (λ(tx) (make-txexpr
      (string->symbol (format "ns:~a" (get-tag tx)))
      (get-attrs tx)
      (get-elements tx))))
; div and h1 are block elements, so this will only affect them
> (decode tx #:block-txexpr-proc add-ns)

'(ns:div "Please" (em "mind the gap") (ns:h1 "Tuesdays only"))

; em is an inline element, so this will only affect it
> (decode tx #:inline-txexpr-proc add-ns)

'(div "Please" (ns:em "mind the gap") (h1 "Tuesdays only"))

; this will affect all elements
> (decode tx #:block-txexpr-proc add-ns #:inline-txexpr-proc add-ns)

'(ns:div "Please" (ns:em "mind the gap") (ns:h1 "Tuesdays only"))

The string-proc, symbol-proc, valid-char-proc, and cdata-proc arguments are procedures that operate on X-expressions that are strings, symbols, valid-chars, and CDATA, respectively. Deliberately, the output contracts for these procedures accept any kind of X-expression (meaning, the procedure can change the X-expression type).

Examples:

; A div with string, entity, character, and cdata elements
> (define tx `(div "Moe" amp 62 ,(cdata #f #f "3 > 2;")))
> (define rulify (λ(x) '(hr)))
; The rulify function is selectively applied to each
> (print (decode tx #:string-proc rulify))

'(div (hr) amp 62 #(struct:cdata #f #f "3 > 2;"))

> (print (decode tx #:symbol-proc rulify))

'(div "Moe" (hr) 62 #(struct:cdata #f #f "3 > 2;"))

> (print (decode tx #:valid-char-proc rulify))

'(div "Moe" amp (hr) #(struct:cdata #f #f "3 > 2;"))

> (print (decode tx #:cdata-proc rulify))

'(div "Moe" amp 62 (hr))

Finally, the tags-to-exclude argument is a list of tags that will be exempted from decoding. Though you could get the same result by testing the input within the individual decoding functions, that’s tedious and potentially slower.

Examples:

> (define tx '(p "I really think" (em "italics") "should be lowercase."))
> (decode tx #:string-proc (λ(s) (string-upcase s)))

'(p "I REALLY THINK" (em "ITALICS") "SHOULD BE LOWERCASE.")

> (decode tx #:string-proc (λ(s) (string-upcase s)) #:exclude-tags '(em))

'(p "I REALLY THINK" (em "italics") "SHOULD BE LOWERCASE.")

The tags-to-exclude argument is useful if you’re decoding source that’s destined to become HTML. According to the HTML spec, material within a <style> or <script> block needs to be preserved literally. In this example, if the CSS and JavaScript blocks are capitalized, they won’t work. So exclude '(style script), and problem solved.

Examples:

> (define tx '(body (h1 [[class "Red"]] "Let's visit Planet Telex.")
  (style [[type "text/css"]] ".Red {color: green;}")
  (script [[type "text/javascript"]] "var area = h * w;")))
> (decode tx #:string-proc (λ(s) (string-upcase s)))

'(body

  (h1 ((class "Red")) "LET'S VISIT PLANET TELEX.")

  (style ((type "text/css")) ".RED {COLOR: GREEN;}")

  (script ((type "text/javascript")) "VAR AREA = H * W;"))

> (decode tx #:string-proc (λ(s) (string-upcase s))
  #:exclude-tags '(style script))

'(body

  (h1 ((class "Red")) "LET'S VISIT PLANET TELEX.")

  (style ((type "text/css")) ".Red {color: green;}")

  (script ((type "text/javascript")) "var area = h * w;"))

2.1 Block

Because it’s convenient, Pollen categorizes tagged X-expressions into two categories: block and inline. Why is it convenient? When using decode, you often want to treat the two categories differently. Not that you have to. But this is how you can.

parameter

(project-block-tags)  (listof txexpr-tag?)

(project-block-tags block-tags)  void?
  block-tags : (listof txexpr-tag?)
 = html-block-tags
A parameter that defines the set of tags that decode will treat as blocks. This parameter is initialized with the HTML block tags, namely:

(address article aside audio blockquote body canvas dd div dl fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hgroup noscript ol output p pre section table tfoot ul video)

procedure

(register-block-tag tag)  void?

  tag : txexpr-tag?
Adds a tag to project-block-tags so that block-txexpr? will report it as a block, and decode will process it with block-txexpr-proc rather than inline-txexpr-proc.

Pollen tries to do the right thing without being told. But this is the rare case where you have to be explicit. If you introduce a tag into your markup that you want treated as a block, you must use this function to identify it, or you will get spooky behavior later on.

For instance, detect-paragraphs knows that block elements in the markup shouldn’t be wrapped in a p tag. So if you introduce a new block element called bloq without registering it as a block, misbehavior will follow:

Examples:

> (define (paras tx) (decode tx #:txexpr-elements-proc detect-paragraphs))
> (paras '(body "I want to be a paragraph." "\n\n" (bloq "But not me.")))

'(body (p "I want to be a paragraph.") (p (bloq "But not me.")))

; Wrong: bloq should not be wrapped

But once you register bloq as a block, order is restored:

Examples:

> (define (paras tx) (decode tx #:txexpr-elements-proc detect-paragraphs))
> (register-block-tag 'bloq)
> (paras '(body "I want to be a paragraph." "\n\n" (bloq "But not me.")))

'(body (p "I want to be a paragraph.") (bloq "But not me."))

; Right: bloq is treated as a block

If you find the idea of registering block tags unbearable, good news. The project-block-tags include the standard HTML block tags by default. So if you just want to use things like div and p and h1–h6, you’ll get the right behavior for free.

Examples:

> (define (paras tx) (decode tx #:txexpr-elements-proc detect-paragraphs))
> (paras '(body "I want to be a paragraph." "\n\n" (div "But not me.")))

'(body (p "I want to be a paragraph.") (div "But not me."))

procedure

(block-txexpr? v)  boolean?

  v : any/c
Predicate that tests whether v is a tagged X-expression, and if so, whether the tag is among the project-block-tags. If not, it is treated as inline. To adjust how this test works, use register-block-tag.

2.2 Typography

An assortment of typography & layout functions, designed to be used with decode. These aren’t hard to write. So if you like these, use them. If not, make your own.

procedure

(whitespace? v)  boolean?

  v : any/c
A predicate that returns #t for any stringlike v that’s entirely whitespace, but also the empty string, as well as lists and vectors that are made only of whitespace? members. Following the regexp-match convention, whitespace? does not return #t for a nonbreaking space. If you prefer that behavior, use whitespace/nbsp?.

Examples:

> (whitespace? "\n\n   ")

#t

> (whitespace? (string->symbol "\n\n   "))

#t

> (whitespace? "")

#t

> (whitespace? '("" "  " "\n\n\n" " \n"))

#t

> (define nonbreaking-space (format "~a" #\u00A0))
> (whitespace? nonbreaking-space)

#f

procedure

(whitespace/nbsp? v)  boolean?

  v : any/c
Like whitespace?, but also returns #t for nonbreaking spaces.

Examples:

> (whitespace/nbsp? "\n\n   ")

#t

> (whitespace/nbsp? (string->symbol "\n\n   "))

#t

> (whitespace/nbsp? "")

#t

> (whitespace/nbsp? '("" "  " "\n\n\n" " \n"))

#t

> (define nonbreaking-space (format "~a" #\u00A0))
> (whitespace/nbsp? nonbreaking-space)

#t

procedure

(smart-quotes str)  string?

  str : string?
Convert straight quotes in str to curly according to American English conventions.

Examples:

> (define tricky-string
  "\"Why,\" she could've asked, \"are we in O‘ahu watching 'Mame'?\"")
> (display tricky-string)

"Why," she could've asked, "are we in O‘ahu watching 'Mame'?"

> (display (smart-quotes tricky-string))

“Why,” she could’ve asked, “are we in O‘ahu watching ‘Mame’?”

procedure

(smart-dashes str)  string?

  str : string?
In str, convert three hyphens to an em dash, and two hyphens to an en dash, and remove surrounding spaces.

Examples:

> (define tricky-string "I had a few --- OK, like 6--8 --- thin mints.")
> (display tricky-string)

I had a few --- OK, like 6--8 --- thin mints.

> (display (smart-dashes tricky-string))

I had a few—OK, like 6–8—thin mints.

; Monospaced font not great for showing dashes, but you get the idea

procedure

(detect-linebreaks tagged-xexpr-elements 
  [#:separator linebreak-sep 
  #:insert linebreak]) 
  txexpr-elements?
  tagged-xexpr-elements : txexpr-elements?
  linebreak-sep : string? = world:linebreak-separator
  linebreak : xexpr? = '(br)
Within tagged-xexpr-elements, convert occurrences of linebreak-sep ("\n" by default) to linebreak, but only if linebreak-sep does not occur between blocks (see block-txexpr?). Why? Because block-level elements automatically display on a new line, so adding linebreak would be superfluous. In that case, linebreak-sep just disappears.

Examples:

> (detect-linebreaks '(div "Two items:" "\n" (em "Eggs") "\n" (em "Bacon")))

'(div "Two items:" (br) (em "Eggs") (br) (em "Bacon"))

> (detect-linebreaks '(div "Two items:" "\n" (div "Eggs") "\n" (div "Bacon")))

'(div "Two items:" (div "Eggs") (div "Bacon"))

procedure

(detect-paragraphs elements 
  [#:separator paragraph-sep 
  #:tag paragraph-tag 
  #:linebreak-proc linebreak-proc]) 
  txexpr-elements?
  elements : txexpr-elements?
  paragraph-sep : string? = world:paragraph-separator
  paragraph-tag : symbol? = 'p
  linebreak-proc : (txexpr-elements? . -> . txexpr-elements?)
   = detect-linebreaks
Find paragraphs within elements, as denoted by paragraph-sep, and wrap them with paragraph-tag, unless the element is already a block-txexpr? (because in that case, the wrapping is superfluous). Thus, as a consequence, if paragraph-sep occurs between two blocks, it’s ignored.

The paragraph-tag argument sets the tag used to wrap paragraphs.

The linebreak-proc argument allows you to use a different linebreaking procedure other than the usual detect-linebreaks.

Examples:

> (detect-paragraphs '("First para" "\n\n" "Second para"))

'((p "First para") (p "Second para"))

> (detect-paragraphs '("First para" "\n\n" "Second para" "\n" "Second line"))

'((p "First para") (p "Second para" (br) "Second line"))

> (detect-paragraphs '("First para" "\n\n" (div "Second block")))

'((p "First para") (div "Second block"))

> (detect-paragraphs '((div "First block") "\n\n" (div "Second block")))

'((div "First block") (div "Second block"))

> (detect-paragraphs '("First para" "\n\n" "Second para") #:tag 'ns:p)

'((ns:p "First para") (ns:p "Second para"))

> (detect-paragraphs '("First para" "\n\n" "Second para" "\n" "Second line")
  #:linebreak-proc (λ(x) (detect-linebreaks x #:insert '(newline))))

'((p "First para") (p "Second para" (newline) "Second line"))

3 File

 (require pollen/file) package: pollen

A utility module that provides functions for working with Pollen source and output files. The tests rely on file extensions specified in pollen/world.

Pollen handles six kinds of source files:

Preprocessor, with file extension .pp.

Markup, with file extension .pm.

Template, with file extension .pt.

Null, with file extension .p.

Scribble, with file extension .scrbl.

For each kind of Pollen source file, the corresponding output file is generated by removing the extension from the name of the source file. So the preprocessor source file default.css.pp would become default.css. Scribble files work differently — the corresponding output file is the source file but with an html extension rather than scrbl. So pollen.scrbl would become pollen.html.

procedure

(preproc-source? v)  boolean?

  v : any/c

procedure

(markup-source? v)  boolean?

  v : any/c

procedure

(template-source? v)  boolean?

  v : any/c

procedure

(null-source? v)  boolean?

  v : any/c

procedure

(scribble-source? v)  boolean?

  v : any/c

procedure

(pagetree-source? v)  boolean?

  v : any/c
Test whether v is a path representing a source file of the specified type, based on file extension.

Examples:

> (preproc-source? "main.css.pp")

#t

> (markup-source? "default.html.pm")

#t

> (template-source? "main.html.pt")

#t

> (null-source? "index.html.p")

#t

> (scribble-source? "file.scrbl")

#t

> (pagetree-source? "index.ptree")

#t

procedure

(has-preproc-source? v)  boolean?

  v : any/c

procedure

(has-markup-source? v)  boolean?

  v : any/c

procedure

(has-template-source? v)  boolean?

  v : any/c

procedure

(has-null-source? v)  boolean?

  v : any/c

procedure

(has-scribble-source? v)  boolean?

  v : any/c
Test whether v is the output path for an existing source file of the specified type.

procedure

(has/is-preproc-source? v)  boolean?

  v : any/c

procedure

(has/is-markup-source? v)  boolean?

  v : any/c

procedure

(has/is-template-source? v)  boolean?

  v : any/c

procedure

(has/is-null-source? v)  boolean?

  v : any/c

procedure

(has/is-scribble-source? v)  boolean?

  v : any/c
Test whether v is a path representing a source file of the specified type, or is the output path for an existing source file of the specified type. In other words, has/is-preproc-source? is equivalent to (or (preproc-source? v) (has-preproc-source? v)).

procedure

(->preproc-source-path p)  path?

  p : pathish?

procedure

(->markup-source-path p)  path?

  p : pathish?

procedure

(->template-source-path p)  path?

  p : pathish?

procedure

(->null-source-path p)  path?

  p : pathish?

procedure

(->scribble-source-path p)  path?

  p : pathish?
Convert an output path p into the source path of the specified type that would produce this output path. This function simply generates a path for a file — it does not ask whether the file exists.

Examples:

> (define name "default.html")
> (->preproc-source-path name)

#<path:default.html.pp>

> (->markup-source-path name)

#<path:default.html.pm>

> (->template-source-path name)

#<path:default.html.pt>

> (->scribble-source-path name)

#<path:default.scrbl>

> (->null-source-path name)

#<path:default.html.p>

procedure

(->output-path p)  path?

  p : pathish?
Convert a source path p into its corresponding output path. This function simply generates a path for a file — it does not ask whether the file exists.

Examples:

> (->output-path "main.css.pp")

#<path:main.css>

> (->output-path "default.html.pm")

#<path:default.html>

> (->output-path "index.html.p")

#<path:index.html>

> (->output-path "file.scrbl")

#<path:file.html>

4 Pagetree

 (require pollen/pagetree) package: pollen

A pagetree is a hierarchical list of Pollen output files. A pagetree source file has the extension .ptree. A pagetree provides a convenient way of separating the structure of the pages from the page sources, and navigating around this structure.

Pagetrees are made of pagenodes. Usually these pagenodes will be names of output files in your project. (If you think it would’ve been more logical to just call them “pages,” perhaps. When I think of a web page, I think of a file on a disk. Whereas pagenodes may — and often do — refer to files that don’t yet exist.)

Books and other long documents are usually organized in a structured way — at minimum they have a sequence of pages, but more often they have sections with subsequences within. Individual Pollen source files don’t know anything about how they’re connected to other files. In theory, you could maintain this information within each source file. This would be a poor use of human energy. Let the pagetree figure it out.

procedure

(pagetree? possible-pagetree)  boolean?

  possible-pagetree : any/c
Test whether possible-pagetree is a valid pagetree. It must be a txexpr? where all elements are pagenode?, and each is unique within possible-pagetree (not counting the root node).

Examples:

> (pagetree? '(root index.html))

#t

> (pagetree? '(root duplicate.html duplicate.html))

#f

> (pagetree? '(root index.html "string.html"))

#f

> (define nested-ptree '(root 1.html 2.html (3.html 3a.html 3b.html)))
> (pagetree? nested-ptree)

#t

> (pagetree? `(root index.html ,nested-ptree (subsection.html more.html)))

#t

; Nesting a subtree twice creates duplication
> (pagetree? `(root index.html ,nested-ptree (subsection.html ,nested-ptree)))

#f

procedure

(validate-pagetree possible-pagetree)  pagetree?

  possible-pagetree : any/c
Like pagetree?, but raises a descriptive error if possible-pagetree is invalid, and otherwise returns possible-pagetree itself.

Examples:

> (validate-pagetree '(root (mama.html son.html daughter.html) uncle.html))

'(root (mama.html son.html daughter.html) uncle.html)

> (validate-pagetree `(root (,+ son.html daughter.html) uncle.html))

#f

> (validate-pagetree '(root (mama.html son.html son.html) mama.html))

validate-pagetree: items aren’t unique: (son.html mama.html)

procedure

(pagenode? possible-pagenode)  boolean?

  possible-pagenode : any/c
Test whether possible-pagenode is a valid pagenode. A pagenode can be any symbol? that is not whitespace/nbsp? Every leaf of a pagetree is a pagenode. In practice, your pagenodes will likely be names of output files.

Pagenodes are symbols (rather than strings) so that pagetrees will be valid tagged X-expressions, which is a more convenient format for validation & processing.

Examples:

; Three symbols, the third one annoying but valid
> (map pagenode? '(symbol index.html |   silly   |))

'(#t #t #t)

; A number, a string, a txexpr, and a whitespace symbol
> (map pagenode? '(9.999 "index.html" (p "Hello") |    |))

'(#f #f #f #f)

procedure

(pagenodeish? v)  boolean?

  v : any/c
Return #t if v can be converted with ->pagenode.

Example:

> (map pagenodeish? '(9.999 "index.html" |    |))

'(#t #t #f)

procedure

(->pagenode v)  pagenode?

  v : pagenodeish?
Convert v to a pagenode.

Examples:

> (map pagenodeish? '(symbol 9.999 "index.html" |  silly  |))

'(#t #t #t #t)

> (map ->pagenode '(symbol 9.999 "index.html" |  silly  |))

'(symbol |9.999| index.html |  silly  |)

4.1 Navigation

parameter

(current-pagetree)  pagetree?

(current-pagetree pagetree)  void?
  pagetree : pagetree?
 = #f
A parameter that defines the default pagetree used by pagetree navigation functions (e.g., parent-pagenode, chidren, et al.) if another is not explicitly specified. Initialized to #f.

procedure

(parent p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the parent pagenode of p within pagetree. Return #f if there isn’t one.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (parent 'son.html)

'mama.html

> (parent "mama.html")

'root

> (parent (parent 'son.html))

'root

> (parent (parent (parent 'son.html)))

#f

procedure

(children p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the child pagenodes of p within pagetree. Return #f if there aren’t any.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (children 'mama.html)

'(son.html daughter.html)

> (children 'uncle.html)

#f

> (children 'root)

'(mama.html uncle.html)

> (map children (children 'root))

'((son.html daughter.html) #f)

procedure

(siblings p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Find the sibling pagenodes of p within pagetree. The list will include p itself. But the function will still return #f if pagetree is #f.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (siblings 'son.html)

'(son.html daughter.html)

> (siblings 'daughter.html)

'(son.html daughter.html)

> (siblings 'mama.html)

'(mama.html uncle.html)

procedure

(previous p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)

procedure

(previous* p [pagetree])  (or/c #f (listof pagenode?))

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Return the pagenode immediately before p. For previous*, return all the pagenodes before p, in sequence. In both cases, return #f if there aren’t any pagenodes. The root pagenode is ignored.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (previous 'daughter.html)

'son.html

> (previous 'son.html)

'mama.html

> (previous (previous 'daughter.html))

'mama.html

> (previous 'mama.html)

#f

> (previous* 'daughter.html)

'(mama.html son.html)

> (previous* 'uncle.html)

'(mama.html son.html daughter.html)

procedure

(next p [pagetree])  (or/c #f pagenode?)

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)

procedure

(next* p [pagetree])  (or/c #f (listof pagenode?))

  p : (or/c #f pagenodeish?)
  pagetree : pagetree? = (current-pagetree)
Return the pagenode immediately after p. For next*, return all the pagenodes after p, in sequence. In both cases, return #f if there aren’t any pagenodes. The root pagenode is ignored.

Examples:

> (current-pagetree '(root (mama.html son.html daughter.html) uncle.html))
> (next 'son.html)

'daughter.html

> (next 'daughter.html)

'uncle.html

> (next (next 'son.html))

'uncle.html

> (next 'uncle.html)

#f

> (next* 'mama.html)

'(son.html daughter.html uncle.html)

> (next* 'daughter.html)

'(uncle.html)

4.2 Utilities

procedure

(pagetree->list pagetree)  list?

  pagetree : pagetree?
Convert pagetree to a simple list. Equivalent to a pre-order depth-first traversal of pagetree.

procedure

(in-pagetree? pagenode [pagetree])  boolean?

  pagenode : pagenode?
  pagetree : pagetree? = (current-pagetree)
Report whether pagenode is in pagetree.

procedure

(path->pagenode p)  pagenode?

  p : pathish?
Convert path p to a pagenode — meaning, make it relative to world:current-project-root, run it through ->output-path, and convert it to a symbol. Does not tell you whether the resultant pagenode actually exists in the current pagetree (for that, use in-pagetree?).

5 Render

 (require pollen/render) package: pollen

Rendering is how Pollen source files get converted into output.

procedure

(render source-path [template-path])  bytes?

  source-path : complete-path?
  template-path : (or/c #f complete-path?) = #f
Renders source-path. The rendering behavior depends on the type of source file:

A pollen/pre file is rendered without a template.

A pollen/markup or pollen/markdown file is rendered with a template. If no template is provided with template-path, Pollen finds one using get-template-for.

Be aware that rendering with a template uses include-template within eval. For complex pages, it can be slow the first time. Caching is used to make subsequent requests faster.

For those panicked at the use of eval, please don’t be. As the author of include-template has already advised, “If you insist on dynamicism” — and yes, I do insist — “there is always eval.

procedure

(render-to-file source-path    
  [template-path    
  output-path])  void?
  source-path : complete-path?
  template-path : (or/c #f complete-path?) = #f
  output-path : (or/c #f complete-path?) = #f
Like render, but saves the file to output-path, overwriting whatever was already there. If no output-path is provided, it’s derived from source-path using ->output-path.

procedure

(render-to-file-if-needed source-path    
  [template-path    
  output-path    
  #:force force-render?])  void?
  source-path : complete-path?
  template-path : (or/c #f complete-path?) = #f
  output-path : (or/c #f complete-path?) = #f
  force-render? : boolean? = #f
Like render-to-file, but the render only happens if one of these conditions exist:
  1. The force-render? flag — set with the #:force keyword — is #t.

  2. No file exists at output-path. (Thus, an easy way to force a render of a particular output-path is to delete it.)

  3. Either source-path or template-path have changed since the last trip through render.

  4. One or more of the project requires have changed.

If none of these conditions exist, output-path is deemed to be up to date, and the render is skipped.

procedure

(render-batch source-paths ...)  void?

  source-paths : (listof pathish?)
Render multiple source-paths in one go. This can be faster than (for-each render source-paths) if your source-paths rely on a common set of templates. Templates may have their own source files that need to be compiled. If you use render, the templates will be repeatedly (and needlessly) re-compiled. Whereas if you use render-batch, each template will only be compiled once.

procedure

(render-pagetree pagetree)  void?

  pagetree : pagetree?
(render-pagetree pagetree-source)  void?
  pagetree-source : pathish?
Using pagetree, or a pagetree loaded from pagetree-source, render the pages in that pagetree using render-batch.

procedure

(get-template-for source-path)  (or/c #f complete-path?)

  source-path : complete-path?
Find a template file for source-path, with the following priority:
  1. If the metas for source-path have a key for template, then use the value of this key.

  2. If this key doesn’t exist, or if it points to a nonexistent file, look for a default template in the project directory with the name main.[output extension].pt. Meaning, if source-path is intro.html.pm, the output path would be intro.html, so the default template would be main.html.pt.

  3. If this file doesn’t exist, use the fallback template as a last resort.

This function is called when a template is needed, but a template-path argument is missing (for instance, in render or render-to-file).

6 Template

 (require pollen/template) package: pollen

Convenience functions for templates. These are automatically imported into the eval environment when rendering with a template (see render).

This module also provides everything from sugar/coerce/value.

procedure

(->html xexpr)  string?

  xexpr : xexpr?
Convert xexpr to an HTML string. Similar to xexpr->string, but consistent with the HTML spec, text that appears within script or style blocks will not be escaped.

Examples:

> (define tx '(root (script "3 > 2") "Why is 3 > 2?"))
> (xexpr->string tx)

"<root><script>3 &gt; 2</script>Why is 3 &gt; 2?</root>"

> (->html tx)

"<root><script>3 > 2</script>Why is 3 &gt; 2?</root>"

Be careful not to pass existing HTML strings into this function, because the angle brackets will be escaped. Fine if that’s what you want, but you probably don’t.

Examples:

> (define tx '(p "You did" (em "what?")))
> (->html tx)

"<p>You did<em>what?</em></p>"

> (->html (->html tx))

"&lt;p&gt;You did&lt;em&gt;what?&lt;/em&gt;&lt;/p&gt;"

procedure

(select key value-source)  (or/c #f txexpr-element?)

  key : symbolish?
  value-source : (or/c hash? txexpr? pagenode? pathish?)

procedure

(select* key value-source)  (or/c #f (listof txexpr-element?))

  key : symbolish?
  value-source : (or/c hash? txexpr? pagenode? pathish?)
Find matches for key in value-source, first by looking in its metas (using select-from-metas) and then by looking in its doc (using select-from-doc). With select, you get the first result; with select*, you get them all. In both cases, you get #f if there are no matches.

procedure

(select-from-metas key meta-source)  (or/c #f txexpr-element?)

  key : symbolish?
  meta-source : (or/c hash? pagenodeish? pathish?)
Look up the value of key in meta-source. The meta-source argument can be either a set of metas (i.e., a hash) or a pagenode?, from which metas are pulled. If no value exists for key, you get #f.

Examples:

> (module ice-cream pollen/markup
  '(div (question "Flavor?")
    (answer "Chocolate chip") (answer "Maple walnut"))
    '(meta ((template "sub.xml.pt")))
    '(meta ((target "print"))))
; Import doc & metas from 'ice-cream submodule
> (require 'ice-cream)
> (select-from-metas 'template  metas)

"sub.xml.pt"

> ('target . select-from-metas . metas)

"print"

> (select-from-metas 'nonexistent-key metas)

#f

procedure

(select-from-doc key doc-source)  (or/c #f txexpr-element?)

  key : symbolish?
  doc-source : (or/c txexpr? pagenodeish? pathish?)
Look up the value of key in doc-source. The doc-source argument can be either be a doc (i.e., a txexpr) or a pagenode?, from which doc is pulled. If no value exists for key, you get #f.

Examples:

> (module gelato pollen/markup
  '(div (question "Flavor?")
    (answer "Nocciola") (answer "Pistachio"))
    '(meta ((template "sub.xml.pt")))
    '(meta ((target "print"))))
; Import doc & metas from 'gelato submodule
> (require 'gelato)
> (select-from-doc 'question  doc)

'("Flavor?")

> ('answer . select-from-doc . doc)

'("Nocciola" "Pistachio")

> (select-from-doc 'nonexistent-key doc)

#f

7 Tag

 (require pollen/tag) package: pollen

Convenience functions for working with tags.

procedure

(make-tag-function id)  (-> txexpr?)

  id : txexpr-tag?
Make a tag function for id. As arguments, a tag function takes an optional set of X-expression attributes (txexpr-attrs?) followed by X-expression elements (txexpr-elements?). From these, the tag function creates a tagged X-expression using id as the tag.

Examples:

> (require pollen/tag)
> (define beaucoup (make-tag-function 'em))
> (beaucoup "Bonjour")

'(em "Bonjour")

> (beaucoup '((id "greeting")) "Bonjour")

'(em ((id "greeting")) "Bonjour")

Entering attributes this way can be cumbersome. So for convenience, a tag function provides an alternative: any symbol + string pairs at the front of your expression will be interpreted as attributes, if the symbols are followed by a colon. If you leave out the colon, the symbols will be interpreted as part of the content of the tag.

Examples:

> (require pollen/tag)
> (define beaucoup (make-tag-function 'em))
> (beaucoup 'id: "greeting" 'class: "large" "Bonjour")

'(em ((id "greeting") (class "large")) "Bonjour")

; Don't forget the colons
> (beaucoup 'id "greeting" 'class "large" "Bonjour")

'(em id "greeting" class "large" "Bonjour")

; Don't forget to provide a value for each attribute
> (beaucoup 'id: 'class: "large" "Bonjour")

'(em id: class: "large" "Bonjour")

Pollen also uses this function to provide the default behavior for undefined tags. See #%top.

8 Top

 (require pollen/top) package: pollen

You’ll probably never invoke this module directly. But it’s implicitly imported into every Pollen markup file. And if you don’t know what it does, you might end up surprised by some of the behavior you get.

syntax

(#%top . id)

In standard Racket, #%top is the function of last resort, called when id is not bound to any value. As such, it typically reports a syntax error.

Examples:

; Let's call em without defining it
> (em "Bonjour")

em: undefined;

 cannot reference undefined identifier

; (em "Bonjour") is being converted to ((#%top . em) "Bonjour")
; So calling ((#%top . em) "Bonjour") will give the same result
> ((#%top . em) "Bonjour")

em: undefined;

 cannot reference undefined identifier

In the Pollen markup environment, however, this behavior is annoying. Because when you’re writing X-expressions, you don’t necessarily want to define all your tags ahead of time.

So Pollen redefines #%top. For convenience, Pollen’s version of #%top assumes that an undefined tag should just refer to an X-expression beginning with that tag (and uses make-tag-function to provide this behavior):

Examples:

; Again, let's call em without defining it, but using pollen/top
> (require pollen/top)
> (em "Bonjour")

'(em "Bonjour")

; (em "Bonjour") is still being converted to ((#%top . em) "Bonjour")
; But now, ((#%top . em) "Bonjour") gives a different result
> ((#%top . em) "Bonjour")

'(em "Bonjour")

The good news is that this behavior means you use any tag you want in your markup without defining it in advance. You can still attach a function to the tag later, which will automatically supersede #%top.

Examples:

> (define (em x) `(span ((style "font-size:100px")) ,x))
> (em "Bonjour")

'(span ((style "font-size:100px")) "Bonjour")

The bad news is that you’ll never get an “undefined identifier” error. These undefined identifiers will happily sail through and be converted to tags.

Examples:

> (require pollen/top)
> (define (em . xs) `(span ((style "font-size:100px")) ,@xs))
; There's a typo in my tag
> (erm "Bonjour")

'(erm "Bonjour")

This isn’t a bug. It’s just a natural consequence of how Pollen’s #%top works. It can, however, make debugging difficult sometimes. Let’s suppose my markup depends on very-important-function, which I don’t import correctly.

Examples:

> (require pollen/top)
> (module vif racket/base
      (define (very-important-function . xs) `(secrets-of-universe ,@xs)))
; Forgot to (require 'vif)
> (very-important-function "Bonjour")

'(very-important-function "Bonjour")

So the undefined-function bug goes unreported. Again, that’s not a bug in Pollen — there’s just no way for it to tell the difference between an identifier that’s deliberately undefined and one that’s inadvertently undefined. If you want to guarantee that you’re invoking a defined identifier, use def/c.

syntax

(def/c id)

Invoke id if it’s a defined identifier, otherwise raise an error. This form reverses the behavior of #%top (in other words, it restores default Racket behavior).

Recall this example from before. In standard Racket, you get an undefined-identifier error.

Examples:

> (module vif racket/base
      (define (very-important-function . xs) `(secrets-of-universe ,@xs)))
; Forgot to (require 'vif)
> (very-important-function "Bonjour")

very-important-function: undefined;

 cannot reference undefined identifier

But with pollen/top, the issue is not treated as an error.

Examples:

> (require pollen/top)
> (module vif racket/base
      (define (very-important-function . xs) `(secrets-of-universe ,@xs)))
; Forgot to (require 'vif)
> (very-important-function "Bonjour")

'(very-important-function "Bonjour")

By adding def/c, we restore the usual behavior, guaranteeing that we get the defined version of very-important-function or nothing.

Examples:

> (require pollen/top)
> (module vif racket/base
      (define (very-important-function . xs) `(secrets-of-universe ,@xs)))
; Forgot to (require 'vif)
> ((def/c very-important-function) "Bonjour")

very-important-function: undefined;

 cannot reference undefined identifier

9 World

 (require pollen/world) package: pollen

A set of global values and parameters that are used throughout the Pollen system. If you don’t like the defaults I’ve picked, change them.

All identifiers are exported with the prefix world:, and are so documented below.

value

world:main-pollen-export : symbol? = 'doc

value

world:meta-pollen-export : symbol? = 'metas

The two exports from a compiled Pollen source file.

value

world:project-require : string? = "project-require.rkt"

File implicitly required into every Pollen source file from its directory.

parameter

(world:check-project-requires-in-render?)  boolean?

(world:check-project-requires-in-render? check?)  void?
  check? : boolean?
 = #t
A parameter that determines whether the world:project-require file is checked for changes on every pass through render. (Can be faster to turn this off if you don’t need it.) Initialized to #t.

value

world:server-extras-dir : string? = "server-extras"

Name of directory where server support files live.

parameter

(world:current-server-extras-path)  path?

(world:current-server-extras-path dir)  void?
  dir : path?
 = #f
A parameter that reports the path to the directory of support files for the development server. Initialized to #f, but set to a proper value when pollen/server runs.

value

world:preproc-source-ext : symbol? = 'pp

value

world:markup-source-ext : symbol? = 'pm

value

world:markdown-source-ext : symbol? = 'pmd

value

world:null-source-ext : symbol? = 'p

value

world:pagetree-source-ext : symbol? = 'ptree

value

world:template-source-ext : symbol? = 'pt

value

world:scribble-source-ext : symbol? = 'scrbl

File extensions for Pollen source files.

File extensions that are eligible for decoding.

value

world:mode-auto : symbol? = 'auto

value

world:mode-preproc : symbol? = 'pre

value

world:mode-markup : symbol? = 'markup

value

world:mode-markdown : symbol? = 'markdown

value

world:mode-pagetree : symbol? = 'ptree

Mode indicators for the Pollen reader and parser.

value

world:default-pagetree : string? = "index.ptree"

Pagetree that Pollen dashboard loads by default in each directory.

value

world:pagetree-root-node : symbol? = 'pagetree-root

Name of the root node in a decoded pagetree. It’s ignored by the code, so its only role is to clue you in that you’re looking at something that came out of the pagetree decoder.

value

world:command-marker : char? = #\◊

The magic character that indicates a Pollen command, function, or variable.

value

world:default-template-prefix : string? = "main"

Prefix of the default template.

value

world:fallback-template : string? = "fallback.html.pt"

Name of the fallback template (i.e., the template used to render a Pollen markup file when no other template can be found).

value

world:template-meta-key : symbol? = 'template

Meta key used to store a template name for that particular source file.

value

world:newline : string? = "\n"

value

world:linebreak-separator : string? = world:newline

value

world:paragraph-separator : string? = "\n\n"

Default separators used in decoding.

value

world:dashboard-css : string? = "poldash.css"

CSS file used for the dashboard.

value

world:paths-excluded-from-dashboard : (listof path?)

 = (map string->path (list "poldash.css" "compiled"))
Paths not shown in the Pollen dashboard.