documentation updates plus related bugfixes

pull/111/head
Matthew Butterick 8 years ago
parent f00ad0d0c8
commit fbaf2c98d4

@ -1,8 +1,7 @@
#lang racket/base
(require (for-syntax racket/base syntax/strip-context racket/syntax "../setup.rkt" "split-metas.rkt")
"to-string.rkt" "../pagetree.rkt" "splice.rkt" "../setup.rkt") ; need world here to resolve PARSER-MODE-ARG
"to-string.rkt" "../pagetree.rkt" "splice.rkt" "../setup.rkt" ) ; need world here to resolve PARSER-MODE-ARG
(provide (all-defined-out))
(require sugar/debug)
(define-syntax-rule (define+provide-module-begin-in-mode PARSER-MODE-ARG)
(begin
@ -46,7 +45,7 @@
[(eq? parser-mode 'MODE-PAGETREE) decode-pagetree]
[(eq? parser-mode 'MODE-MARKUP) (λ(xs) (apply ROOT xs))] ; if `root` undefined, it becomes a default tag function
[(eq? parser-mode 'MODE-MARKDOWN)
(λ(xs) (apply ROOT ((dynamic-require 'markdown 'parse-markdown) (apply string-append (map to-string xs)))))]
(λ(xs) (apply ROOT (map strip-empty-attrs ((dynamic-require 'markdown 'parse-markdown) (apply string-append (map to-string xs))))))]
[else (λ(xs) (apply string-append (map to-string xs)))])] ; string output for preprocessor
;; drop leading newlines, as they're often the result of `defines` and `requires`
[doc-elements (or (memf (λ(ln) (not (equal? ln NEWLINE))) DOC-RAW) null)]

@ -9,11 +9,11 @@
(define spliceable? (λ(x) (and (pair? x) (eq? (car x) splicing-tag))))
(define not-null-string? (λ(x) (not (and (string? x) (= (string-length x) 0)))))
(let loop ([x x])
(if (list? x)
(apply append (map (λ(x) ((if (spliceable? x)
cdr
list) (loop x))) (filter not-null-string? x)))
x)))
(if (list? x)
(apply append (map (λ(x) ((if (spliceable? x)
cdr
list) (loop x))) (filter not-null-string? x)))
x)))
(module+ test
(require rackunit)
@ -22,4 +22,19 @@
(check-equal? (splice `((,splice-signal-tag 1 (,splice-signal-tag 2 "" (,splice-signal-tag 3 (div 4 (,splice-signal-tag 5))) 6) "" 7)))
'(1 2 3 (div 4 5) 6 7))
(check-equal? (splice `((,splice-signal-tag "foo" "" "bar"))) '("foo" "bar"))
(check-equal? (splice null) null))
(check-equal? (splice null) null))
(define (strip-empty-attrs x)
(let loop ([x x])
(if (list? x)
;; this will strip all empty lists.
;; in practice, they would only appear in attrs position
(map loop (filter (λ(x) (not (null? x))) x))
x)))
(module+ test
(check-equal? (strip-empty-attrs '(p ())) '(p))
(check-equal? (strip-empty-attrs '(p () "foo")) '(p "foo"))
(check-equal? (strip-empty-attrs '(p () (em () "foo") "bar")) '(p (em "foo") "bar")))

@ -7,21 +7,21 @@
(define (meta? x) ; meta has form (define-meta key value)
(and (list? x) (>= (length x) 3) (eq? (car x) meta-key)))
(define (non-meta?/gather x)
(or (not (meta? x))
(and (set! matches (cons x matches)) #f)))
(define rest
(let loop ([x (if (list? tree) tree (list tree))])
(if (list? x)
(map loop (filter non-meta?/gather x))
x)))
(cond
[(meta? x) (set! matches (cons x matches)) #f]
[(list? x) (filter (λ(x) x) (map loop x))]
[else x])))
(values (apply hasheq (apply append (map cdr matches))) rest))
(values (apply hasheq (apply append (reverse (map cdr matches)))) rest))
(module+ test
(require rackunit)
(define x '(root (div (define-meta foo "bar") "hi") "zim" "zam"))
(define-values (metas rest) (split-metas x 'define-meta))
(check-equal? metas '#hasheq((foo . "bar")))
(check-equal? rest '(root (div "hi") "zim" "zam")))
(let-values ([(metas rest) (split-metas '(root (div (define-meta foo "bar") "hi") "zim" (define-meta foo "boing") "zam") 'define-meta)])
(check-equal? metas '#hasheq((foo . "boing")))
(check-equal? rest '(root (div "hi") "zim" "zam")))
(let-values ([(metas rest) (split-metas '(root (define-meta dog "Roxy") (define-meta dog "Lex")) 'define-meta)])
(check-equal? metas '#hasheq((dog . "Lex"))))
(let-values ([(metas rest) (split-metas '(root (define-meta dog "Roxy") (div (define-meta dog "Lex"))) 'define-meta)])
(check-equal? metas '#hasheq((dog . "Lex")))))

@ -6,4 +6,8 @@
[(string? x) x]
[(or (null? x) (void? x)) ""]
[(or (symbol? x) (number? x) (path? x) (char? x)) (format "~a" x)]
;; special handling for procedures, because if a procedure reaches this func,
;; it usually indicates a failed attempt to use a tag function.
;; meaning, it's more useful to raise an error.
[(procedure? x) (error 'pollen "Can't convert procedure ~a to string" x)]
[else (format "~v" x)]))

@ -1 +1 @@
1454367371
1454721844

@ -2,7 +2,7 @@
@title{Acknowledgments}
One curious aspect of free software is that you can appropriate the benefits of other people's work while making it look like your own. No such legerdemain here. Whatever effort I've put into Pollen is dwarfed by the epic accomplishments of the Racket development team. I thank all of them — especially @link["https://www.cs.utah.edu/~mflatt/"]{Matthew Flatt}, @link["http://faculty.cs.byu.edu/~jay/home/"]{Jay McCarthy}, and @link["http://www.ccs.neu.edu/home/matthias/"]{Matthias Felleisen} — for making this tremendous tool available, for adding several features I suggested, and for patiently answering my dumb questions over the months.
One curious aspect of free software is that you can appropriate the benefits of other people's work while making it look like your own. No such legerdemain here. Whatever effort I've put into Pollen is dwarfed by the epic accomplishments of the Racket development team. I thank all of them — especially @link["http://www.barzilay.org"]{Eli Barzilay}, @link["https://www.cs.utah.edu/~mflatt/"]{Matthew Flatt}, @link["http://faculty.cs.byu.edu/~jay/home/"]{Jay McCarthy}, @link["https://www.eecs.northwestern.edu/~robby/"]{Robby Findler}, @link["https://www.soic.indiana.edu/faculty-research/directory/profile.html?profile_id=311"]{Sam Tobin-Hochstadt}, and @link["http://www.ccs.neu.edu/home/matthias/"]{Matthias Felleisen} — for making this tremendous tool available, for adding several features I suggested, and for patiently answering my dumb questions over the months.
Thank you to Greg Hendershott for his Markdown parser.

@ -2,7 +2,7 @@
@title[#:tag "big-picture"]{The big picture}
A summary of the key components & concepts of the Pollen publishing system and how they fit together. If you've completed the @secref["quick-tour"], this will lend some context to what you saw. The next tutorials will make more sense if you read this first.
A summary of the key components & concepts of the Pollen publishing system and how they fit together. If you've completed the @secref["quick-tour"], this will lend some context to what you saw. The upcoming @seclink["first-tutorial"]{tutorials} will make more sense if you read this first.
@section[#:tag "the-book-is-a-program"]{The book is a program}
@ -35,13 +35,13 @@ This is the core design principle of Pollen. Consistent with this principle, Pol
@section{Development environment}
The Pollen development environment has three main pieces: the DrRacket code editor, the project server, and the command-line tool.
The Pollen development environment has three main pieces: the DrRacket code editor, the project server, and the command line.
@itemlist[
@item{@bold{Edit source files with DrRacket.} DrRacket is Racket's GUI code editor. Sure, you can also use a generic text editor. But DrRacket lets you immediately run your source and see if it works.}
@item{@bold{Preview & test web pages with the Pollen project server.} Pollen has a built-in development web server called the @defterm{project server}. After you start the project server, you can preview your web pages within any web browser, allowing you to test them with maximum accuracy.}
@item{@bold{Preview & test web pages with the Pollen project server.} Pollen has a built-in development web server called the @seclink["Using_the_project_server"]{@defterm{project server}}. After you start the project server, you can preview your web pages within any web browser, allowing you to test them with maximum accuracy.}
@item{@bold{Write the docs.} The project server can recognize and render Scribble files, so you can use it as a previewing tool while you're writing your documentation.}
@ -53,7 +53,7 @@ The Pollen development environment has three main pieces: the DrRacket code edit
@section{A special data structure for HTML}
Unlike other programming languages, Pollen (and Racket) internally represent HTML with something called @secref["X-expressions" #:doc '(lib "pollen/scribblings/pollen.scrbl")]. An X-expression is simply a list that represents an HTML @defterm{element}, meaning a thing with an opening tag, a closing tag, and content in between. Like HTML elements, X-expressions can be nested. Unlike HTML elements, X-expressions have no closing tag, they use parentheses to denote the start and end, and text elements are put inside quotes.
Unlike other programming languages, Pollen (and Racket) internally represent HTML with something called @secref["X-expressions"]. An X-expression is simply a list that represents an HTML @defterm{element}, meaning a thing with an opening tag, a closing tag, and content in between. Like HTML elements, X-expressions can be nested. Unlike HTML elements, X-expressions have no closing tag, they use parentheses to denote the start and end, and text elements are put inside quotes.
For example, consider this HTML element:
@ -63,7 +63,7 @@ As a Racket X-expression, this would be written:
@nested[#:style 'code-inset]{@verbatim{(body (h1 "Hello world") (p "Nice to " (i "see") " you."))}}
More will be said about X-expressions. But a couple advantages should be evident already. First, without the redundant angle brackets, the X-expression is arguably more readable than the equivalent HTML. Second, an X-expression is preferable to treating HTML as a simple string, because it preserves the internal structure of the element.
More will be said about X-expressions. But a several advantages should be evident already. First, without the redundant angle brackets, the X-expression is arguably more readable than the equivalent HTML. Second, an X-expression is preferable to treating HTML as a simple string, because it preserves the internal structure of the element. Third, an X-expression is a native data type in Racket.
@section{Pollen command syntax}
@ -75,10 +75,10 @@ As mentioned above, a Pollen source file is not code with text embedded in it, b
@item{@bold{If you can write text, you can program in Pollen.} Really. As you already found out in the @secref["quick-tour"], this is a valid Pollen program:
@codeblock{
#lang pollen
Hello setup: how are you on this fine summer day?
Bonjour, tout le monde: comment ça va?
}}
@item{@bold{Commands start with ◊.} A simple rule: if a piece of text starts with @litchar{◊}, it's treated as a command; otherwise it's treated as ordinary text.}
@item{@bold{Commands start with ◊.} A simple rule: if something in a Pollen source file starts with @litchar{◊}, it's treated as a command; otherwise it's treated as ordinary text.}
@item{@bold{Write commands in Pollen mode or Racket mode.} Commands can use two equivalent notation systems: either Pollen's text-oriented command syntax, or standard Racket syntax.}
@ -108,7 +108,7 @@ If you want to apply a particular page format to multiple sources of content —
@itemlist[
@item{@bold{Templates can be any format.} Usually Pollen templates will be HTML. But they don't have to be.}
@item{@bold{Templates can be any format.} Usually Pollen templates will be HTML. But they don't have to be. Templates can generate any kind of file — either text-based (XML) or not (PDF).}
@item{@bold{Markdown authoring mode.} Pollen has a built-in Markdown parser, so you can import Markdown sources into a Pollen publication.}

@ -1,7 +1,7 @@
#lang scribble/manual
@(require scribble/bnf scribble/eval "utils.rkt" "mb-tools.rkt"
(for-syntax racket/base)
(for-label rackunit pollen/core pollen/setup pollen/render pollen/template (only-in scribble/reader
(for-label rackunit pollen/core pollen/setup pollen/cache pollen/tag pollen/render pollen/template (only-in scribble/reader
use-at-readtable)))
@(define read-eval (make-base-eval))
@ -17,23 +17,25 @@
Pollen uses a special character — the @italic{lozenge}, which looks like this: ◊ — to mark commands within a Pollen source file. So when you put a ◊ in your source, whatever comes next will be treated as a command. If you don't, it will just be interpreted as plain text.
@section{The lozenge glyph (◊)}
@section[#:tag "the-lozenge"]{The lozenge (◊)}
I chose the lozenge as the command character because a) it appears in almost every font, b) it's barely used in ordinary typesetting, c) it's not used in any programming language that I know of, and d) its shape and color allow it to stand out easily in code without being distracting.
Here's how you type it:
If you're using DrRacket, you can use the @onscreen{Insert Command Char} button at the top of the editing window to — you guessed it — insert the command character.
@bold{Mac}: option + shift + V
If you're using a different editor, here's how you type it:
@bold{Windows}: holding down alt, type 9674 on the num pad
@bold{Mac}: option + shift + V
@(linebreak)@bold{Windows}: holding down alt, type 9674 on the num pad
@(linebreak)@bold{Ubuntu}: ctrl + shift + U, then 25CA
@bold{Ubuntu}: ctrl + shift + U, then 25CA
@subsection{``But I don't want to use it ...''}
Still, if you don't want to use the lozenge as your command character, you can set Pollen's @racket[default-command-char] value to whatever character you want (see also @seclink["setup-overrides"]).
Fine, but you have to pick @italic{something} as your command character. If you don't like this one, you can override it within a project — see @seclink["setup-overrides"].
@margin-note{Scribble uses the @"@" sign as a delimiter. It's not a bad choice if you only work with Racket files. But as you use Pollen to work on other kinds of text-based files that commonly contain @"@" signs — HTML pages especially — it gets cumbersome. So I changed it.}
Still, it's not like I'm asking you to learn @link["http://c2.com/cgi/wiki?AplLanguage"]{APL}. Racket supports Unicode, so it's a little silly to artificially limit ourselves to ASCII.
But don't knock the lozenge till you try it.
My advice: don't knock the lozenge till you try it.
@subsection{Lozenge helpers}
@ -42,7 +44,7 @@ But don't knock the lozenge till you try it.
When you use DrRacket, you'll see a button in the toolbar that says @onscreen{Insert command char}. This will insert the lozenge (or whatever command character you've defined for your project).
@subsubsection{AHK script}
@subsubsection{AHK script for Windows}
Courtesy of @link["https://github.com/maphew"]{Matt Wilkie}: ``Here's a working AHK script to have double-tap backtick send the lozenge character. It took way more time than I want to think about, once started I couldn't let go.''
@ -103,25 +105,26 @@ Courtesy of @link["https://github.com/lerichard95"]{Richard Le}: ``If you're usi
;; Bind key to M-\ a la DrRacket for lambda
(global-set-key "\M-\\" 'insert-lozenge)}
@;--------------------------------------------------------------------
@section[#:tag "the-two-command-modes"]{The two command modes: Pollen mode & Racket mode}
@section[#:tag "the-two-command-styles"]{The two command styles: Pollen style & Racket style}
Pollen commands can be entered in one of two modes: @italic{Pollen mode} or @italic{Racket mode}. Both modes start with a lozenge (@litchar["◊"]):
Pollen commands can be entered in one of two styles: @italic{Pollen style} or @italic{Racket style}. Both styles start with a lozenge (@litchar["◊"]):
@racketblock[
@#,BNF-seq[@litchar["◊"] @nonterm{command name} @litchar{[} @nonterm{Racket arguments ...} @litchar{]} @litchar["{"] @nonterm{text argument} @litchar["}"]]
@#,BNF-seq[@litchar["◊"] @nonterm{command name} @litchar{[} @nonterm{Racket arguments ...} @litchar{]} @litchar["{"] @nonterm{text body ...} @litchar["}"]]
@#,BNF-seq[@litchar["◊"]
@litchar{(} @nonterm{Racket expression} @litchar{)}]
]
@bold{Pollen-mode commands}
@bold{Pollen-style commands}
A Pollen-mode command has the three possible parts after the @litchar["◊"]:
A Pollen-style command has the three possible parts after the @litchar["◊"]:
@itemlist[
@item{The @italic{command name} appears immediately after the @litchar["◊"]. Typically it's a short word.}
@item{The @italic{Racket arguments} appear between square brackets. Pollen is partly an interface to the Racket programming language. These arguments are entered using Racket conventions — e.g., a @code{string of text} needs to be put in quotes as a @code{"string of text"}. If you like programming, you'll end up using these frequently. If you don't, you won't.}
@item{The @italic{text argument} appears between braces (aka curly brackets). You can put any ordinary text here. Unlike with the Racket arguments, you don't put quotes around the text.}
@item{The @italic{Racket arguments} appear between square brackets. Pollen is partly an interface to the Racket programming language. These arguments are entered using Racket conventions — e.g., a string of text needs to be put in quotes as a @code{"string of text"}. If you like programming, you'll end up using these arguments frequently. If you don't, you won't.}
@item{The @italic{text body} appears between braces (aka curly brackets). You can put any ordinary text here. Unlike with the Racket arguments, you don't put quotes around the text.}
]
Each of the three parts is optional. You can also nest commands within each other. However:
@ -131,7 +134,7 @@ Each of the three parts is optional. You can also nest commands within each othe
@item{Whatever parts you use must always appear in the order above.}
]
Here are a few examples of correct Pollen-mode commands:
Here are a few examples of correct Pollen-style commands:
@codeblock{
#lang pollen
@ -147,40 +150,40 @@ And some incorrect examples:
@codeblock{
#lang pollen
◊tag {Text inside the tag.} ; space between first and second parts
◊tag[Text inside the tag] ; text argument needs to be within braces
◊tag[Text inside the tag] ; text body needs to be within braces
◊tag{Text inside the tag}[#:attr "value"] ; wrong order
}
The next section describes each of these parts in detail.
@bold{Racket-mode commands}
@bold{Racket-style commands}
If you're familiar with Racket expressions, you can use the Racket-mode commands to embed them within Pollen source files. It's simple: any Racket expression can become a Pollen command by adding @litchar["◊"] to the front. So in Racket, this code:
If you're familiar with Racket expressions, you can use the Racket-style commands to embed them within Pollen source files. It's simple: any Racket expression becomes a Pollen command by adding @litchar["◊"] to the front. So in Racket, this code:
@codeblock{
#lang racket
(define song "Revolution")
(format "~a #~a" song (* 3 3))
#lang racket
(define band "Level")
(format "~a ~a" band (* 2 3 7))
}
Can be converted to Pollen like so:
@codeblock{
#lang pollen
◊(define song "Revolution")
◊(format "~a #~a" song (* 3 3))
#lang pollen
◊(define band "Level")
◊(format "~a ~a" band (* 2 3 7))
}
And in DrRacket, they produce the same output:
@repl-output{Revolution #9}
@repl-output{Level 42}
Beyond that, there's not much to say about Racket mode — any valid expression you can write in Racket will also be a valid Racket-mode Pollen command.
Beyond that, there's not much to say about Racket style — any valid Racket expression will also be a valid Racket-style Pollen command.
@bold{The relationship of Pollen mode and Racket mode}
@bold{The relationship of Pollen style and Racket style}
Even if you don't plan to write a lot of Racket-mode commands, you should be aware that under the hood, Pollen is converting all commands in Pollen mode to Racket mode. So a Pollen-mode command that looks like this:
Even if you don't plan to write a lot of Racket-style commands, you should be aware that under the hood, Pollen is converting all Pollen-style commands to Racket style. So a Pollen-style command that looks like this:
@codeblock[#:keep-lang-line? #f]{
#lang pollen
@ -188,38 +191,39 @@ Even if you don't plan to write a lot of Racket-mode commands, you should be awa
}
Is actually being turned into a Racket-mode command like this:
Is actually being turned into this Racket-style command:
@codeblock[#:keep-lang-line? #f]{
#lang racket
(headline #:size 'enormous "Man Bites Dog!")
}
Thus a Pollen-mode command is just an alternate way of writing a Racket-mode command. (More broadly, all of Pollen is just an alternate way of using Racket.)
Thus a Pollen-style command is just an alternate way of writing a Racket-style command. (More broadly, all of Pollen is just an alternate way of using Racket.)
The corollary is that you can always write Pollen commands using whichever mode is more convenient or readable. For instance, the earlier example, written in the Racket mode:
The corollary is that you can always write Pollen commands using whichever style is more convenient or readable. For instance, the earlier example, written in the Racket style:
@codeblock{
#lang pollen
◊(define song "Revolution")
◊(format "~a #~a" song (* 3 3))
◊(define band "Level")
◊(format "~a ~a" band (* 2 3 7))
}
Can be rewritten using Pollen mode:
Can be rewritten in Pollen style:
@codeblock{
#lang pollen
◊define[song]{Revolution}
◊format["~a #~a" song (* 3 3)]
◊define[band]{Level}
◊format["~a ~a" band (* 2 3 7)]
}
And it will work the same way.
You can combine the two styles in whatever way makes sense to you. I typically reserve Pollen-style commands for when I'm mixing commands into textual material. Meaning, I prefer @code{◊headline[#:size 'enormous]{Man Bites Dog!}} over @code{◊(headline #:size 'enormous "Man Bites Dog!")}. But when I'm writing or using traditional Racket functions, I find Racket-style commands to be more readable (because they correspond to ordinary Racket syntax, and thus can be moved between Pollen and Racket source files more easily). So I prefer @code{◊(define band "Level")} over @code{◊define[band]{Level}}.
@;--------------------------------------------------------------------
@subsection{The command name}
In Pollen, you'll typically use the command name for one of four purposes:
In Pollen, you'll likely use a command for one of these purposes:
@itemlist[
@item{To invoke a tag function.}
@ -229,10 +233,11 @@ In Pollen, you'll typically use the command name for one of four purposes:
@item{To insert a comment.}
]
@;--------------------------------------------------------------------
Let's look at each kind of use.
@subsubsection{Invoking tag functions}
By default, Pollen treats every command name as a @italic{tag function}. The default tag function creates a tagged X-expression with the command name as the tag, and the text argument as the content.
By default, Pollen treats every command name as a @italic{tag function}. The default tag function creates a @seclink["What_s_a_txexpr_" #:doc '(lib "txexpr/scribblings/txexpr.scrbl")]{tagged X-expression} with the command name as the tag, and the text body as the content.
@codeblock{
#lang pollen
@ -246,10 +251,10 @@ To streamline markup, Pollen doesn't restrict you to a certain set of tags, nor
@codeblock{
#lang pollen
◊utterlyridiculoustagname{Oh really?}
◊utterly-ridiculous-tag-name{Oh really?}
}
@repl-output{'(utterlyridiculoustagname "Oh really?")}
@repl-output{'(utterly-ridiculous-tag-name "Oh really?")}
@ -270,7 +275,6 @@ the expected number of arguments does not match the given number
What to do? Read on.
@;--------------------------------------------------------------------
@subsubsection{Invoking other functions}
Though every command name starts out as a default tag function, it doesn't necessarily end there. You have two options for invoking other functions: defining your own, or invoking others from Racket.
@ -296,7 +300,7 @@ We can define @code{strong} to do something else, like add to the text:
@repl-output{'(strong "Hey! Listen up! Fancy Sauce, $1")}
The replacement function has to accept any arguments that might get passed along, but it doesn't have to do anything with them. For instance, this function definition won't work because @code{strong} is going to get a text argument that it's not defined to handle:
The replacement function has to accept any arguments that might get passed along, but it doesn't have to do anything with them. For instance, this function definition won't work because @code{strong} is going to get a text body that it's not defined to handle:
@codeblock{
#lang pollen
@ -322,6 +326,9 @@ Whereas in this version, @code{strong} accepts an argument called @code{text}, b
@repl-output{'(fib "1 1 2 3 5 8 13 ...")}
@margin-note{The text body can pass an indefinite number of arguments. A well-designed tag function should be able to handle them, unlike these synthetic examples. For a more realistic example, see @secref["the-text-body"].}
You can attach any behavior to a command name. As your project evolves, you can also update the behavior of a command name. In that way, Pollen commands become a set of hooks to which you can attach more elaborate processing.
@bold{Using Racket functions}
@ -330,19 +337,19 @@ You aren't limited to functions you define. Any function from Racket, or any Rac
@codeblock|{
#lang pollen
range[1 20]
(range 1 20)
}|
@repl-output{'(range 1 20)}
Hold on — that's not what we want. Where's the list of numbers? The problem here is that we didn't explicitly import the @racketmodname[racket/list] library, which contains the definition for @racket[range]. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without @racketmodname[racket/list], Pollen just thinks we're trying to use @code{range} as a tag function (and if we had been, then @repl-output{'(range 1 20)} would've been the right result).
Hold on — that's not what we want. Where's the list of numbers? The problem here is that we for to import the @racketmodname[racket/list] library, which contains the definition for @racket[range]. (If you need to find out what library contains a certain function, the Racket documentation will tell you.) Without @racketmodname[racket/list], Pollen just thinks we're trying to use @code{range} as a tag function (and if we had been, then @val['(range 1 20)] would've been the right result).
We fix this by using the @racket[require] command to bring in the @racketmodname[racket/list] library, which contains the @racket[range] we want:
@codeblock|{
#lang pollen
◊(require racket/list)
range[1 20]
(range 1 20)
}|
@repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}
@ -353,7 +360,7 @@ Of course, you can also invoke Racket functions indirectly, by attaching them to
#lang pollen
◊(require racket/list)
◊(define (rick start finish) (range start finish))
rick[1 20]
(rick 1 20)
}|
@repl-output{'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)}
@ -361,7 +368,7 @@ Of course, you can also invoke Racket functions indirectly, by attaching them to
Let's return to the problem that surfaced in the last section — the fact that some command names can't be used as tag functions because they're already being used for other things. You can work around this by defining your own tag function with a non-conflicting name.
For instance, suppose we want to use @code{map} as a tag even though Racket is using it for its own function called @racket[map]. First, we invent a command name that doesn't conflict. Let's call it @code{my-map}. As you learned above, Pollen will treat a new command name as a tag function by default:
For instance, suppose we want to use @code{map} as a tag even though Racket is using it for its own function called @racket[map]. First, we invent a command name that doesn't conflict. Let's call it @id{my-map}. As you learned above, Pollen will treat a new command name as a tag function by default:
@codeblock|{
#lang pollen
@ -371,7 +378,7 @@ For instance, suppose we want to use @code{map} as a tag even though Racket is u
@repl-output{'(my-map "How I would love this to be a map.")}
But @code{my-map} is not the tag we want. We need to define @code{my-map} to be a tag function for @code{map}. We can do this with the Pollen helper @racket[default-tag-function]. That function lives in @racket[pollen/tag], so we @racket[require] that too:
But @id{my-map} is not the tag we want. We need to define @id{my-map} to be a tag function for @id{map}. We can do this with the Pollen helper @racket[default-tag-function]. That function lives in @racketmodname[pollen/tag], so we @racket[require] that too:
@codeblock|{
@ -387,7 +394,6 @@ Problem solved.
@;--------------------------------------------------------------------
@subsubsection{Inserting the value of a variable}
A Pollen command name usually refers to a function, but it can also refer to a @italic{variable}, which is a data value. Once you define the variable, you can insert it into your source by using the ◊ notation without any other arguments:
@ -401,9 +407,7 @@ The value of foo is ◊foo
@repl-output{The value of foo is bar}
Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. This won't work, because a variable is not a function:
@margin-note{To understand what happens here, recall the relationship between Pollen's command modes. The Pollen-mode command @code{◊foo[]} becomes the Racket-mode command @code{(foo)}, which after variable substitution becomes @code{("bar")}. If you try to evaluate @code{("bar")} — e.g., in DrRacket — you'll get the same error.}
Be careful — if you include arguments, even blank ones, Pollen will treat the command name as a function. For instance, this next example won't work, because a variable is not a function:
@codeblock|{
@ -419,7 +423,10 @@ expected a procedure that can be applied to arguments
  arguments...: [none]}
The reason we can simply drop @code{◊foo} into the text argument of another Pollen command is that the variable @code{foo} holds a string (i.e., a text value).
To understand what happens here, recall the relationship between Pollen's command styles. The Pollen-style command @code{◊foo[]} becomes the Racket-style command @code{(foo)}, which after variable substitution becomes @code{("bar")}. If you try to evaluate @code{("bar")} — e.g., in DrRacket — you'll get the same error.
The reason we can simply insert @code{◊foo} into the text body of another Pollen command is that the variable @code{foo} holds a string (i.e., a text value).
In preprocessor source files, Pollen will convert a variable to a string in a sensible way. For instance, numbers are easily converted:
@ -431,7 +438,7 @@ The value of zam is ◊zam
@repl-output{The value of zam is 42}
@margin-note{In an unsaved DrRacket file, or a file without a special Pollen source extension, the @tt{#lang pollen} designation invokes the Pollen preprocessor by default. You can explicitly invoke preprocessor mode by starting a file with @tt{#lang pollen/pre}. See also @secref["Preprocessor___pp_extension_"].}
@margin-note{In an unsaved DrRacket file, or a file without a special Pollen source extension, the @tt{#lang pollen} designation invokes the Pollen preprocessor by default. You can explicitly invoke preprocessor style by starting a file with @tt{#lang pollen/pre}. See also @secref["Preprocessor___pp_extension_"].}
If the variable holds a container datatype (like a @racket[list], @racket[hash], or @racket[vector]), Pollen will produce the Racket text representation of the item. Here, @code{zam} is a @racket[list] of integers:
@ -449,7 +456,7 @@ This feature is included for your convenience. But in general, your readers won'
#lang pollen
◊(require racket/string)
◊(define zam (list 1 2 3))
The value of zam is ◊string-join[(map number->string zam)]{ and }
The value of zam is ◊(string-join (map number->string zam) " and ")
}|
@repl-output{The value of zam is 1 and 2 and 3}
@ -462,9 +469,9 @@ Pollen will still produce an error if you try to convert an esoteric value to a
The value of zam is ◊zam
}|
@errorblock{Pollen decoder: can't convert #<procedure:+> to string}
@errorblock{pollen: Can't convert procedure #<procedure:+> to string}
Moreover, Pollen will not perform @italic{any} automatic text conversion in Pollen markup source files. Suppose we take the example above — which worked as a preprocessor source file — and change the language to @racket[pollen/markup]:
In Pollen markup, the result is different. A Pollen markup file makes an X-expression, not text, so Pollen doesn't perform @italic{any} automatic text conversion — that's your job. Suppose we take the example above — which worked with the Pollen preprocessor — and change the language to @racketmodname[pollen/markup]:
@codeblock|{
#lang pollen/markup
@ -478,6 +485,19 @@ This time, the file will produce an error:
pollen markup error: in '(root "The value of zam is " (1 2 3)), '(1 2 3) is not a valid element (must be txexpr, string, symbol, XML char, or cdata)
}
But the second example above, with the explicit conversion using @racket[string-join], does work in Pollen markup, because strings are valid X-expressions:
@codeblock|{
#lang pollen/markup
◊(require racket/string)
◊(define zam (list 1 2 3))
The value of zam is ◊(string-join (map number->string zam) " and ")
}|
@repl-output{'(root "The value of zam is " "1 and 2 and 3")}
@margin-note{See @secref["File_formats"] for more about the differences between Pollen dialects.}
One special case to know about. In the examples above, there's a word space between the variable and the other text. But suppose you need to insert a variable into text so that there's no space in between. The simple ◊ notation won't work, because it won't be clear where the variable name ends and the text begins.
For instance, suppose we want to use a variable @code{edge} next to the string @code{px}:
@ -488,7 +508,7 @@ For instance, suppose we want to use a variable @code{edge} next to the string
p { margin-left: ◊edgepx; }
}|
@errorblock{Pollen decoder: can't convert #<procedure:...t/pollen/tag.rkt:6:2> to string}
@errorblock{pollen: Can't convert procedure #<procedure:pollen-tag:edgepx> to string}
The example fails because Pollen reads the whole string after the @litchar{◊} as the single variable name @code{edgepx}. Since @code{edgepx} isn't defined, it's treated as a tag function, and since Pollen can't convert a function to a string, we get an error.
@ -515,21 +535,20 @@ The value of edge is ◊|edge| pixels}
@;--------------------------------------------------------------------
@subsubsection{Inserting metas}
@italic{Metas} are keyvalue pairs embedded in a source file that are not included in the main output when the source is compiled. Rather, they're gathered and exported as a separate hash table called, unsurprisingly, @racket[metas]. This hashtable is a good place to store information about the document that you might want to use later (for instance, a list of topic categories that the document belongs to).
@italic{Metas} are keyvalue pairs embedded in a source file that are not included in the main output when the source is compiled. Rather, they're gathered and exported as a separate hash table called, unsurprisingly, @id{metas}. This hashtable is a good place to store information about the document that you might want to use later (for instance, a list of topic categories that the document belongs to).
@margin-note{Pollen occasionally uses metas internally. For instance, the @racket[get-template-for] function will look in the metas of a source file to see if a template is explicitly specified. The @racket[pollen/template] module also contains functions for working with metas, such as @racket[select-from-metas].}
@margin-note{Pollen occasionally uses metas internally. For instance, the @racket[get-template-for] function will look in the metas of a source file to see if a template is explicitly specified. The @racketmodname[pollen/core] module also contains functions for working with metas, such as @racket[select-from-metas].}
To make a meta, you create a tag with the special @racket[define-meta] name. Then you have two choices: you can either embed the key-value pair as an attribute, or as a tagged X-expression within the meta (using the key as the tag, and the value as the body):
@codeblock{
#lang pollen
◊define-meta[dog]{Roxy} ; Pollen-mode syntax
◊define-meta[dog]{Roxy} ; Pollen-style syntax
◊some-tag[#:key "value"]{Normal tag}
◊(define-meta cat "Chopper") ; equivalent Racket-mode syntax
◊(define-meta cat "Chopper") ; equivalent Racket-style syntax
◊some-tag[#:key "value"]{Another normal tag}
}
@ -549,7 +568,7 @@ Second, the metas are collected into a hash table that is exported with the name
'#hasheq((dog . "Roxy") (cat . "Chopper") (here-path . "unsaved-editor"))
}
The only key that's automatically defined in every meta table is @code{here-path}, which is the absolute path to the source file. (In this case, because the file hasn't been saved, you'll see the @code{unsaved-editor} name instead.)
The only key that's automatically defined in every meta table is @id{'here-path}, which is the absolute path to the source file. (In this case, because the file hasn't been saved, you'll see the @val{unsaved-editor} name instead.)
Still, you can override this too:
@ -571,7 +590,7 @@ When you run this code, the result will be the same as before, but this time the
}
It doesn't matter how many metas you put in a source file, nor where you put them. They'll all be extracted into the @code{metas} hash table. The order of the metas is not preserved (because order is not preserved in a hash table). But if you have two metas with the same key, the later one will supersede the earlier one:
It doesn't matter how many metas you put in a source file, nor where you put them. They'll all be extracted into the @id{metas} hash table. The order of the metas is not preserved (because order is not preserved in a hash table). But if you have two metas with the same key, the later one will supersede the earlier one:
@codeblock{
#lang pollen
@ -579,28 +598,28 @@ It doesn't matter how many metas you put in a source file, nor where you put the
◊(define-meta dog "Lex")
}
In this case, though there are two metas named @racket[dog] (and they use different forms) only the second one persists:
Though there are two metas named @id{dog}, only the second one persists:
@terminal{
> metas
'#hasheq((dog . "Lex") (here-path . "unsaved-editor"))
}
@bold{Pro tip}: the @racket[metas] hashtable is available when you import a Pollen source file in the usual way, but it's also made available through a submodule called, unsurprisingly, @racket[metas].
@codeblock{
#lang racket/base
(require "pollen-source.rkt") ; doc and metas and everything else
(require (submod "pollen-source.rkt" metas)) ; just metas
}
The @racket[metas] submodule is useful because it gives you access to the @racket[metas] hashtable @italic{without} compiling the rest of the file. So if you need to collect metas from a set of source files — for instance, page titles (for a table of contents) or categories — getting the metas through the submodule is likely to be faster.
@subsubsection{Retrieving metas}
The @id{metas} hashtable is available immediately within the body of your source file. You can use @racket[select] to get values out of @id{metas}.
@;--------------------------------------------------------------------
@subsubsection{Retrieving metas}
@codeblock{
#lang pollen
◊(define-meta dog "Roxy")
◊(select 'dog metas)
}
The @racket[metas] hashtable is available immediately within the body of your source file. You can use @racket[hash-ref] to get values out of @racket[metas].
@repl-output{Roxy}
@id{metas} is an immutable hash, so you can also use immutable-hash functions, like @racket[hash-ref]:
@codeblock{
#lang pollen
@ -608,48 +627,51 @@ The @racket[metas] hashtable is available immediately within the body of your so
◊(hash-ref metas 'dog)
}
@terminal{
Roxy
}
@repl-output{Roxy}
Because the metas are collected first, you can actually invoke a meta before you define it:
@codeblock{
#lang pollen
◊(hash-ref metas 'dog)
◊(select 'dog metas)
◊(define-meta dog "Roxy")
◊(define-meta dog "Spooky")
}
@terminal{
Spooky
}
@repl-output{Spooky}
This can be useful for setting up fields that you want to include in @racket[metas] but also have visible in the body of a document, like a title.
This can be useful for setting up fields that you want to include in @id{metas} but also have visible in the body of a document, like a title.
@codeblock{
#lang pollen/markup
◊(define-meta title "The Amazing Truth")
◊h1{◊(hash-ref metas 'title)}
◊h1{◊(select 'title metas)}
}
The result of this file will be:
@terminal{
'(root (h1 "The Amazing Truth"))
}
@repl-output{'(root (h1 "The Amazing Truth"))}
And the metas:
@terminal{
> metas
'#hasheq((title . "The Amazing Truth") (here-path . "unsaved-editor"))
}
You cannot, however, use @racket[hash-set!] or other similar functions, because @racket[metas] is an immutable hash.
@bold{Pro tip}: Within Pollen, the fastest way to get a @id{metas} hashtable from another source file is to use @racket[cached-metas].
@bold{Pro tip #2}: Outside Pollen, the @id{metas} hashtable is available when you import a Pollen source file in the usual way, but it's also made available through a submodule called, unsurprisingly, @id{metas}.
@codeblock{
#lang racket/base
(require "pollen-source.rkt") ; doc and metas and everything else
(require (submod "pollen-source.rkt" metas)) ; just metas
}
The @id{metas} submodule gives you access to the @id{metas} hashtable @italic{without} compiling the rest of the file. So if you need to harvest metas from a set of source files — for instance, page titles (for a table of contents) or categories — using @racket[require] with the submodule will be faster.
@;--------------------------------------------------------------------
@subsubsection{Inserting a comment}
Two options.
@ -679,10 +701,9 @@ Actually, it's all a comment now
@repl-output{Actually, it's all a comment now}
@;--------------------------------------------------------------------
@subsection{The Racket arguments}
The middle part of a Pollen-mode command contains the @italic{Racket arguments} @litchar{[}between square brackets.@litchar{]} Most often, you'll see these used to pass extra information to commands that operate on text.
The middle part of a Pollen-style command contains the @italic{Racket arguments} @litchar{[}between square brackets.@litchar{]} Most often, you'll see these used to pass extra information to commands that operate on text.
For instance, tag functions. Recall from before that any not-yet-defined command name in Pollen is treated as a tag function:
@ -709,7 +730,7 @@ Here's the hard way. You can type out your list of attributes in Racket format a
@repl-output{'(title ((class "red") (id "first")) "The Beginning of the End")}
But that's a lot of parentheses to think about. So here's the easy way. Anytime you use a tag function, there's a shortcut for inserting attributes. You can enter them as a series of @italic{keyword arguments} between the Racket-argument brackets. The only caveat is that the values for these keyword arguments have to be strings. So taken together, they look like this:
But that's a lot of parentheses to think about. So here's the easy way. Whenever you use a tag function, there's a shortcut for inserting attributes. You can enter them as a series of @italic{keyword arguments} between the Racket-argument brackets. The only caveat is that the values for these keyword arguments have to be strings. So taken together, they look like this:
@codeblock|{
#lang pollen
@ -722,7 +743,7 @@ The string arguments can be any valid Racket expressions that produce strings. F
@codeblock|{
#lang pollen
◊title[#:class (format "~a" (* 6 7)) #:id "first"]{The Beginning of the End}
◊title[#:class (number->string (* 6 7)) #:id "first"]{The Beginning of the End}
}|
@repl-output{'(title ((class "42") (id "first")) "The Beginning of the End")}
@ -754,10 +775,29 @@ When used in custom tag functions, keyword arguments don't have to represent att
'(h6 "Trivial league")
}
@;--------------------------------------------------------------------
@subsection{The text argument}
@bold{Pro tip}: See also @racket[define-tag-function], which automatically converts keyword arguments into attributes before they reach your function:
@codeblock|{
#lang pollen
◊(require pollen/tag)
◊(define-tag-function (heading attrs elems)
(define level (cadr (assq 'level attrs)))
`(,(string->symbol (format "h~a" level)) ,@elems))
◊heading[#:level 1]{Major league}
◊heading[#:level 2]{Minor league}
◊heading[#:level 6]{Trivial league}
}|
@repl-output{'(h1 "Major league")
'(h2 "Minor league")
'(h6 "Trivial league")
}
@subsection[#:tag "the-text-body"]{The text body}
The third part of a Pollen-mode command is the text argument. The text argument @litchar{@"{"}appears between curly braces@litchar{@"}"}. It can contain any text you want. The text argument can also contain other Pollen commands with their own text arguments. And they can contain other Pollen commands ... and so on, all the way down.
The third part of a Pollen-style command is the text body. The text body @litchar{@"{"}appears between curly braces@litchar{@"}"}. It can contain any text you want. The text body can also contain other Pollen commands with their own text body. And they can contain other Pollen commands ... and so on, all the way down.
@codeblock|{
#lang pollen
@ -766,9 +806,9 @@ The third part of a Pollen-mode command is the text argument. The text argument
@repl-output{'(div "Do it again. " (div "And again. " (div "And yet again.")))}
Three small details to know about the text argument.
Three things to know about the text body.
First, the only character that needs special handling in a text argument is the lozenge @litchar{◊}. A lozenge ordinarily marks a new command. So if you want an actual lozenge to appear in the text, you have to escape it by typing @litchar{◊"◊"}.
First, the only character that needs special handling in the text body is the lozenge @litchar{◊}. A lozenge ordinarily marks a new command. So if you want an actual lozenge to appear in the text, you have to escape it by typing @litchar{◊"◊"}.
@codeblock|{
#lang pollen
@ -777,7 +817,7 @@ First, the only character that needs special handling in a text argument is the
@repl-output{'(definition "This is the lozenge: ◊")}
Second, the whitespace-trimming policy. Here's the short version: if there's a carriage return at either end of the text argument, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text argument, with carriage returns on the ends:
Second, the whitespace-trimming policy. Here's the short version: if there's a newline at either end of the text body, it is trimmed, and whitespace at the end of each line is selectively trimmed in an intelligent way. So this text body, with newlines on the ends:
@codeblock|{
#lang pollen
@ -790,7 +830,7 @@ I agree.
@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
Yields the same result as this one:
Yields the same result as this one, without the newlines:
@codeblock|{
#lang pollen
@ -801,10 +841,11 @@ I agree.}
@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
For the long version, please see [future link: Spaces, Newlines, and Indentation].
For the long version, please see @secref["Spaces__Newlines__and_Indentation"
#:doc '(lib "scribblings/scribble/scribble.scrbl")].
Third, within a multiline text argument, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That's why in the last example, we got this:
Third, within a multiline text body, newline characters become individual strings that are not merged with adjacent text. So what you end up with is a list of strings, not a single string. That's why in the last example, we got this:
@repl-output{'(div "Roomy!" "\n" "\n" "I agree.")}
@ -812,7 +853,7 @@ Instead of this:
@repl-output{'(div "Roomy!\n\nI agree.")}
Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that operates on multiline text arguments needs to be able to handle an indefinite number of strings. For instance, this @code{jejune} function only accepts a single argument. It will work with a single-line text argument, because that produces a single string:
Under most circumstances, these two tagged X-expressions will behave the same way. The biggest exception is with functions. A function that might operate on a multiline text body needs to be able to handle an indefinite number of strings. For instance, this @code{jejune} function only accepts a single argument. It will work with a single-line text body, because that produces a single string:
@codeblock|{
#lang pollen
@ -823,7 +864,7 @@ Under most circumstances, these two tagged X-expressions will behave the same wa
@repl-output{'(jejune "Irrational confidence")}
But watch what happens with a multiline text argument:
But watch what happens with a multiline text body:
@codeblock|{
#lang pollen
@ -909,13 +950,11 @@ For numeric entities, you can also use a four-digit Unicode hex number by prefac
Of course, you don't need to use @racket[string->symbol] and @racket[string->number] directly in your source. You can also define tag functions that generate entities. The key point is that to be treated as an entity, the return value must be a symbol or number, rather than a string.
@section{Adding Pollen-mode commands to a Racket file}
@section{Adding Pollen-style commands to a Racket file}
@defmodulelang[pollen/mode]
Just as you can embed any Racket-mode command in a Pollen source file, you can go the other way and embed Pollen-mode commands in a Racket file. For instance, in your @secref["The__pollen_rkt__file"], you may find it convenient to use Pollen mode for certain values.
You enable Pollen mode within your source file by adding @racketmodname[pollen/mode] to your @tt{#lang} line at the top of your source:
Just as you can embed any Racket-style command in a Pollen source file, you can go the other way and embed Pollen-style commands in a Racket file. Just insert @racketmodname[pollen/mode] in the @tt{#lang} line at the top of your source:
@fileblock["pollen.rkt" @codeblock{
#lang pollen/mode racket/base
@ -926,12 +965,12 @@ You enable Pollen mode within your source file by adding @racketmodname[pollen/m
(define (home-link)
(link #:href "index.html" "Click to go home"))
(define (home-link-pollen-mode)
(define (home-link-pollen-style)
◊link[#:href "index.html"]{Click to go home})
}]
Here, both @tt{(home-link)} and @tt{(home-link-pollen-mode)} will produce the same X-expression as a result:
Here, both @tt{(home-link)} and @tt{(home-link-pollen-style)} will produce the same X-expression as a result:
@terminal{'(a ((href "index.html")) "Click to go home")}
@ -939,11 +978,9 @@ Of course, you can use @racketmodname[pollen/mode] in any Racket source file, no
@bold{Major caveat}: @racketmodname[pollen/mode] only works with Pollen's default command character, namely the lozenge (@litchar{◊}). If you've overridden this command character in your @filepath{pollen.rkt} file, your custom command character will work everywhere @italic{except} in @racketmodname[pollen/mode]. This limitation is necessary to prevent the intractable situation where @filepath{pollen.rkt} relies on @racketmodname[pollen/mode], but @racketmodname[pollen/mode] relies on a config setting in @filepath{pollen.rkt}.
Also keep in mind that @racketmodname[pollen/mode] is just a syntactic convenience. It doesn't change any of the underlying semantics of your Racket source file. Your Pollen-mode commands are being translated into Racket commands and compiled along with everything else.
Another good way to use Pollen-mode commands in Racket is for unit tests with @racketmodname[rackunit]. With @racketmodname[pollen/mode], you can write your unit tests in Pollen mode or Racket mode (or mix them).
Also keep in mind that @racketmodname[pollen/mode] is just a syntactic convenience. It doesn't change any of the underlying semantics of your Racket source file. Your Pollen-style commands are being translated into Racket commands and compiled along with everything else.
@margin-note{Unit tests are little one-line tests you put into your code to verify that it does what you expect. You make these with the @racketmodname[rackunit] library, which is beloved by all Racket programmers. For more, see @secref["quick-start" #:doc '(lib "rackunit/scribblings/rackunit.scrbl")].}
Another good way to use Pollen-style commands in Racket is for unit tests with @racketmodname[rackunit]. With @racketmodname[pollen/mode], you can write your unit tests in Pollen style or Racket style (or mix them). This makes it easy to verify that Pollen-style commands will turn into the Racket values that you expect:
@fileblock["pollen.rkt" @codeblock|{
#lang pollen/mode racket/base

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
#lang scribble/manual
@(require scribble/eval pollen/render pollen/setup (for-label racket (except-in pollen #%module-begin) pollen/setup sugar pollen/pagetree))
@(require scribble/eval pollen/render "mb-tools.rkt" pollen/setup (for-label pollen/cache (except-in pollen/top #%top def/c) racket (except-in pollen #%module-begin) pollen/setup sugar pollen/pagetree))
@(define my-eval (make-base-eval))
@(my-eval `(require pollen pollen/file))
@ -14,11 +14,11 @@
@defmodulelang*[(pollen/pre pollen/markdown pollen/markup pollen/ptree)]
The Pollen language is divided into variants, or @italic{dialects}, that are tailored to suit each of the core source formats.
The Pollen language is divided into @italic{dialects} that are tailored to suit each of the core source formats.
These dialects can be invoked one of two ways: either by invoking a specific dialect in the first line of the file (also known as the @tt{#lang} line), or by using the generic @tt{#lang pollen} as the first line, and then the correct dialect will be automatically selected based on the source file extension.
If the @tt{#lang} line specifies a dialect different from the one specified by the file extension, the @tt{#lang} line will take precedence.
If the @tt{#lang} line specifies a dialect different from the one implied by the file extension, the @tt{#lang} line will take precedence.
For ease of use, the behavior of the Pollen language departs from the standard Racket language in several ways. The differences are noted below.
@ -27,28 +27,34 @@ For ease of use, the behavior of the Pollen language departs from the standard R
Commands must start with the special lozenge character @litchar{◊}. Other material is interpreted as plain text. See @secref["pollen-command-syntax"] for more.
You can change the command character for a project by overriding @racket[default-command-char].
@bold{How is this different from Racket?} In Racket, everything is a command, and plain text must be quoted.
@subsection{Any command is valid}
There are no undefined commands in Pollen. If a command has not already been defined, it's treated as a tag function. See @secref["pollen-command-syntax"] for more.
There are no undefined commands in Pollen. If a command has not already been defined, it's treated as a tag function. See @secref["pollen-command-syntax"] and @racketmodname[pollen/top] for more.
@bold{How is this different from Racket?} In Racket, if you try to treat an identifier as a function before defining it with @racket[define], you'll get an error.
@subsection{Standard exports}
By default, every Pollen source file exports two identifiers, which you can access by using the source file with @racket[require]:
By default, every Pollen source file exports two identifiers:
The main export, @racket[doc], contains the output of the file. The type of output depends on the source format (documented below).
@itemlist[
@item{@id{doc} contains the output of the file. The type of output depends on the source format (about which, more below).}
The second export, @racket[metas], is a hashtable of keyvalue pairs with extra information that is extracted from the source. These @racket[metas] will always contain the key @racket['here-path], which returns a string representation of the full path to the source file. Beyond that, the only @racket[metas] are the ones that are specified within the source file (see the source formats below for more detail on how to specify metas).
@item{@id{metas} is a hashtable of keyvalue pairs with extra information that is extracted from the source. These @id{metas} will always contain the key @id{'here-path}, which returns a string representation of the full path to the source file. Beyond that, the only @id{metas} are the ones that are specified within the source file (see the source formats below for more detail on how to specify metas).}
]
As usual, you can use @racket[require], @racket[local-require], or @racket[dynamic-require] to retrieve these values. But within a Pollen project, the faster way is to use @racket[cached-doc] and @racket[cached-metas].
Pollen source files also make the @racket[metas] hashtable available through a submodule, also called @racket[metas]. So rather than importing a source file with @racket[(require "source.html.pm")], you would @racket[(require (submod "source.html.pm" metas))]. Accessing the metas this way avoids fully compiling the source file, and thus will usually be faster.
Pollen source files also make the @id{metas} hashtable available through a submodule, unsurprisingly called @id{metas}. So rather than importing a source file with @racket[(require "source.html.pm")], you can @racket[(require (submod "source.html.pm" metas))]. Accessing the metas this way avoids fully compiling the source file, and thus will usually be faster.
The names @racket[doc] and @racket[metas] can be changed for a project by overriding @racket[default-main-export] and @racket[default-meta-export].
The names @@id{doc} and @@id{metas} can be changed for a project by overriding @racket[default-main-export] and @racket[default-meta-export].
@margin-note{The Pollen rendering system relies on these two identifiers, but otherwise doesn't care how they're generated. Meaning, the code inside your Pollen source file could be @tt{#lang racket} or @tt{#lang whatever}. As long as you manually @racket[provide] those two identifiers and follow the usual file-naming convention, your source file will be usable.}
@margin-note{The Pollen rendering system relies on these two identifiers, but otherwise doesn't care how they're generated. Meaning, the code inside your Pollen source file could be @tt{#lang racket} or @tt{#lang whatever}. As long as you manually @racket[provide] those two identifiers and follow the usual file-naming convention, your source file will be renderable.}
@bold{How is this different from Racket?} In Racket, you must explicitly @racket[define] and then @racket[provide] any values you want to export.
@ -86,7 +92,7 @@ _...source...
Of course, you're better off specifying the preprocessor dialect explicitly rather than relying on this default behavior.
The output of the preprocessor dialect, provided by @racket['doc], is plain text.
The output of the preprocessor dialect, provided by @id{doc}, is plain text. For this reason, the preprocessor will convert everything it finds to text in the least surprising way possible.
@ -157,7 +163,9 @@ Scribble files are recognized by the project server and can be compiled and prev
Files with the null extension are simply rendered as a copy of the file without the extension, so @filepath{index.html.p} becomes @filepath{index.html}.
This can be useful you're managing your project with git. Most likely you'll want to ignore @filepath{*.html} and other file types that are frequently regenerated by the project server. But if you have isolated static files — for instance, a @filepath{index.html} that doesn't have source associated with it — they'll be ignored too. You can cure this problem by appending the null extension to these static files, so they'll be tracked in your source system without actually being source files.
This can be useful you're managing your project with Git. Most likely you'll want to ignore @filepath{*.html} and other file types that are frequently regenerated by the project server. But if you have isolated static files — for instance, a @filepath{index.html} that doesn't have source associated with it — they'll be ignored too. You can cure this problem by appending the null extension to these static files, so they'll be tracked in your source system without actually being source files.
The null extension is also useful for templates — @filepath{template.html} and @filepath{template.html.p} will work the same way.
@ -165,8 +173,8 @@ This can be useful you're managing your project with git. Most likely you'll wan
Pollen relies extensively on the convention of naming source files by adding a source extension to an output-file name. So the Pollen markup source for @filepath{index.html} would be @filepath{index.html.pm}.
This convention occasionally flummoxes other programs that assume a file can only have one extension. If you run into such a situation, you can @italic{escape} the output-file extension using the @racket[default-extension-escape-char], which defaults to the underscore @litchar{_}.
This convention occasionally flummoxes other programs that assume a file can only have one extension. If you run into such a situation, you can @italic{escape} the output-file extension with the underscore character @litchar{_}.
So instead of @filepath{index.html.pm}, your source-file name would be @filepath{index_html.pm}. When this source file is rendered, it will automatically be converted into @filepath{index.html} (meaning, the escaped extension will be converted into a normal file extension).
This alternative-naming scheme is automatically enabled in every project. You can also set the escape character on a per-project basis (see @racket[setup:extension-escape-char]). Pollen will let you choose any character, but of course it would be unwise to pick one with special meaning in your filesystem (for instance, @litchar{/}).
This alternative-naming scheme is automatically enabled in every project. You can also set the escape character on a per-project basis (by overriding @racket[defaul-extension-escape-char]). Pollen will let you choose any character, but of course it would be unwise to pick one with special meaning in your filesystem (for instance, @litchar{/}).

@ -14,7 +14,7 @@ Pollen will run on OS X, Linux, or Windows.
Pollen is not a self-contained GUI program like Adobe InDesign. It's a software package that runs atop the Racket language environment (also a free download).
Your three main tools in Pollen will be a text editor (for those starting out, I recommend @other-doc['(lib "scribblings/drracket/drracket.scrbl")]), a terminal window, and a web browser. The terminal commands you'll be using are simple, but if you haven't used your terminal window before, this is the moment to learn where it is.
Your three main tools in Pollen will be a text editor (for those starting out, I recommend @other-doc['(lib "scribblings/drracket/drracket.scrbl")]), a terminal window, and a web browser. The terminal commands you'll be using are simple, but if you haven't used your terminal window before, this is the moment to learn where it is. (On OS X, your terminal window is called Terminal; on Windows it's called the Windows Command Processor.)
After the initial download, Pollen does not require a network connection.
@ -37,24 +37,37 @@ Welcome to Racket v.@(version).
Type @exec{ctrl+D} to exit.
}
But if you get an error like this:
@terminal{Unrecognized command: racket}
@item{Windows users, when you see instructions that reference @exec{raco} — like the one below — I'll trust you to convert into the appropriate command for your system — assuming defaults, it's likely to be @filepath{C:\Program Files\Racket\raco} (include the surrounding quotes in the command).}
You have a deeper problem with your Racket installation that needs adjustment before continuing (usually a misconfiguration of @code{PATH}).
}
@item{Then, from the command line, install Pollen:
@commandline{raco pkg install pollen}
To check that it worked, try typing @exec{raco pollen version} on the command line, and you should see something like this:
To check that it worked, try typing @exec{raco pollen test} on the command line, and you should see this:
@terminal{~ : raco pollen version
@|pollen:version|
@terminal{~ : raco pollen test
raco pollen is installed correctly
~ :
}
But if you get:
@terminal{raco: Unrecognized command: pollen}
You'll need to fix the problem before proceeding, most likely by reinstalling Pollen.
@margin-note{Windows users, when you see instructions that reference @exec{raco}, I'll trust you to convert into the appropriate command for your system. Assuming defaults, it's likely to be @filepath{C:\Program Files\Racket\raco} (include the surrounding quotes in the command).}
}
@item{Alternatively, you can install Pollen from inside DrRacket with the @onscreen{File > Install Package ...} command.}
@item{Alternatively, you can install Pollen from inside DrRacket with the @menuitem["File" "Install Package ..."] command.}
@item{Either way, Pollen's documentation will be automatically installed.}

@ -47,7 +47,7 @@
(define (noskip-note)
(nested #:style (style "noskip" (list (css-style-addition mb-css) (alt-tag "div")))
(margin-note "Dont skip this section! It explains a concept that's essential to understanding how Pollen works.")))
(margin-note "Dont skip this section! It explains an essential Pollen concept.")))
(define-syntax (image/rp stx)

@ -1,13 +1,15 @@
/* special styles for custom @fileblock function in mb-tools.rkt */
.noskip .refcolumn {
background: #998;
background: #666;
border-color: #333;
color: white;
padding-top: 0.6rem;
}
.noskip .refcontent p {
font-size: 110%;
font-weight: bolder;
line-height: 1.4;
}

@ -199,7 +199,7 @@ six
@section{The automatic pagetree}
In situations where Pollen needs a pagetree but can't find one, it will automatically synthesize a pagetree from a listing of files in the directory. This arises most frequently when @secref["Using_the_dashboard" #:doc '(lib "pollen/scribblings/pollen.scrbl")] in a directory that doesn't contain an explicit @filepath{index.ptree}. This way, you can get going with a project without having to stop for @racketfont{.ptree} housekeeping.
In situations where Pollen needs a pagetree but can't find one, it will automatically synthesize a pagetree from a listing of files in the directory. This arises most frequently when @secref["The_project_dashboard"] in a directory that doesn't contain an explicit @filepath{index.ptree}. This way, you can get going with a project without having to stop for @racketfont{.ptree} housekeeping.
As usual, convenience has a cost. Pollen doesn't know anything about which files in your directory are relevant to the project, so it includes all of them. For instance, if you start your project server on an OS X desktop, you'll see things like @filepath{Thumbs.db} and @filepath{desktop.ini}.
@ -212,7 +212,7 @@ Typically you'll call the pagetree-navigation functions from inside templates, u
@section{Using @filepath{index.ptree} in the dashboard}
When you're using the project server to view the files in a directory, the server will first look for a file called @filepath{index.ptree}. If it finds this pagetree file, it will use it to build the dashboard. If not, then it will synthesize a pagetree using a directory listing. For more on this technique, see @secref["Using_the_dashboard" #:doc '(lib "pollen/scribblings/pollen.scrbl")].
When you're using the project server to view the files in a directory, the server will first look for a file called @filepath{index.ptree}. If it finds this pagetree file, it will use it to build the dashboard. If not, then it will synthesize a pagetree using a directory listing. For more on this technique, see @secref["The_project_dashboard"].
@section{Using pagetrees with @exec{raco pollen render}}

@ -24,25 +24,25 @@ The @seclink["third-tutorial"]{third tutorial} introduced you to the idea that @
@item{Every Pollen tag calls a function with the same name.}
@item{The input values for that function are the attributes and content of the tag.}
@item{The input values for that function are the attributes and elements of the tag.}
@item{The whole tag — tag name, attributes, and content — is replaced with the return value of the called function.}
@item{The whole tag — tag name, attributes, and elements — is replaced with the return value of the called function.}
]
@subsection{Tag-function syntax}
To be safe, let's review what we mean by @italic{tag}, @italic{attributes}, and @italic{content}, in Pollen-mode syntax:
To be safe, let's review what we mean by @italic{tag}, @italic{attributes}, and @italic{elements}, in Pollen-mode syntax:
@codeblock[#:keep-lang-line? #f]{
#lang pollen
A tag alone: ◊get-author-name[]
A tag with content: Adding ◊em{emphasis to words}.
A tag with elements: Adding ◊em{emphasis to words}.
A tag with attributes and content:
A tag with attributes and elements:
◊div['((attr1 "val1")(attr2 "val2"))]{My nice div.}
A tag with attributes and content (alternate syntax):
A tag with attributes and elements (alternate syntax):
◊div[#:attr1 "val1" #:attr2 "val2"]{My nice div.}
}
@ -52,13 +52,13 @@ Let's also recall that these commands can be written in Racket mode equivalently
#lang pollen
A tag alone: ◊(get-author-name)
A tag with content: Adding ◊(em "emphasis to words").
A tag with elements: Adding ◊(em "emphasis to words").
A tag with attributes and content:
A tag with attributes and elements:
◊(div '((attr1 "val1")(attr2 "val2")) "My nice div.")
}
Let's also remember that a tag without attributes or content is interpreted as a value. If that's what you want — for instance, when you @racket[define] a tag to hold a value — then great. But if you @racket[define] a tag as a function, you need to add square brackets to signal that you want to evaluate the function:
Let's also remember that a tag without attributes or elements is interpreted as a value. If that's what you want — for instance, when you @racket[define] a tag to hold a value — then great. But if you @racket[define] a tag as a function, you need to add square brackets to signal that you want to evaluate the function:
@codeblock[#:keep-lang-line? #f]{
#lang pollen

@ -189,7 +189,7 @@ Suppose you want to change the inset to 30%. Without a preprocessor, you'd have
The @code{◊} character is called a @italic{lozenge}. In Pollen, the lozenge is a special character used to denote anything that Pollen should interpret as a command (rather than plain text).
If you're using DrRacket, you can insert a lozenge by clicking the @onscreen{Insert command char ◊} button at the top of your source window. (If you're not using DrRacket, see @seclink["The_lozenge_glyph____"]{these instructions}.)
If you're using DrRacket, you can insert a lozenge by clicking the @onscreen{Insert command char ◊} button at the top of your source window. (If you're not using DrRacket, see @seclink["the-lozenge"]{these instructions}.)
Thus, the command @code{◊(define my-inset "30%")} means ``create the variable @code{my-inset} and assign it the value @racket{30%}.''
@ -276,7 +276,7 @@ But what if you wanted to use Pollen as a preprocessor that outputs a Markdown f
@section{Pollen markup}
If all you need to do is produce basic HTML, Markdown is fine. But if you need to do semantic markup or other kinds of custom markup, it's @seclink["The_case_against_Markdown"]{not flexible enough}.
If all you need to do is produce basic HTML, Markdown is fine. But if you need to do semantic markup or other kinds of custom markup, it's @seclink["the-case-against-markdown"]{not flexible enough}.
In that case, you can use a different authoring mode, called @defterm{Pollen markup}. To use Pollen markup, insert @code{#lang pollen} as the first line of your source as usual, but this time add a @filepath{.pm} file extension.
@ -381,7 +381,7 @@ Now you've seen the key features of Pollen. What do you think?
@item{@italic{``What about pairing a Python template system and Python web server?''} Good idea. I even tried it. But Python template systems don't offer you Python — they offer you limited dialects that aren't very Pythonic. Also, Python's handing of XML-ish data is cumbersome.}
@item{@italic{``Haven't you heard of Jekyll?''} Yes. If everything you need to write is expressible in Markdown, it's great. If you need more than that, you're stuck. (See also @seclink["The_case_against_Markdown"]{my objections to Markdown for books}.)}
@item{@italic{``Haven't you heard of Jekyll?''} Yes. If everything you need to write is expressible in Markdown, it's great. If you need more than that, you're stuck. (See also @seclink["the-case-against-markdown"]{my objections to Markdown for books}.)}
@item{@italic{``Sounds a lot like LaTeX. Why not use that?''} Also a good idea. LaTeX gets a lot of things right. But it's also missing a lot — for instance, Unicode and web publishing.}

@ -47,11 +47,11 @@ Displays a list of available commands.
@section{@exec{raco pollen start}}
Start the project server from the current directory using the default port, which is the value of the parameter @racket[current-server-port] (by default, port @(format "~a" default-project-server-port)).
Start the project server from the current directory using the default port, which is the value of the parameter @racket[current-server-port] (by default, port @id[default-project-server-port]).
This command can be invoked with two optional arguments.
@racket[raco pollen start _path] will start the project server in @racket[_path] rather than the current directory.
@racket[raco pollen start _path] will start the project server from @racket[_path] rather than the current directory (making @racket[_path] its root directory).
@terminal{
> raco pollen start ~/path/to/project/}
@ -107,7 +107,7 @@ Make a copy of the project directory on the desktop, but without any source file
If you're already in your project directory and want to publish somewhere other than the desktop, use @racket[raco pollen publish _. _dest-dir].
You can determine the files that get filtered out in a particular project by using @racket[setup:unpublished-path?].
You can determine the files that get filtered out in a particular project by overriding @racket[default-unpublished-path?].
@section{@exec{raco pollen setup}}
@ -151,3 +151,4 @@ Result is DEBUG
}

@ -1,10 +1,8 @@
#lang scribble/manual
@title{Backstory}
I created Pollen to overcome limitations & frustrations I repeatedly encountered with existing web-publishing tools.
I created Pollen to overcome limitations & frustrations I repeatedly encountered with existing web-publishing tools.
If you agree with my characterization of those problems, then you'll probably like the solution that Pollen offers. If not, then you probably won't.
@ -16,13 +14,13 @@ If you weren't around then, you didn't miss much. Everything about the web was h
That's no longer true. The web is now more than 20 years old. During that time, most parts of the web have improved dramatically — for instance, the connections are faster, the browsers are more sophisticated, and the screens have more pixels.
But one part hasn't improved much: the way we make web pages. Over the years, tools promising to simplify web development have come and mostly gone — from @link["http://www.macobserver.com/reviews/pagemill2.shtml"]{PageMill} to @link["http://www.adobe.com/products/dreamweaver.html"]{Dreamweaver} to @link["http://wordpress.org"]{WordPress} to @link["http://jekyllrb.com"]{Jekyll}. Meanwhile, true web jocks have remained loyal to the original HTML power tool: the humble text editor.
But one part hasn't improved much: the way we make web pages. Over the years, tools promising to simplify web development have come and mostly gone — from @link["http://www.macobserver.com/reviews/pagemill2.shtml"]{PageMill} to @link["http://www.adobe.com/products/dreamweaver.html"]{Dreamweaver} to @link["http://www.squarespace.com"]{Squarespace}. Meanwhile, serious web jocks have remained loyal to the original HTML power tool: the humble text editor.
In one way, this makes sense. Web pages are mostly made of text-based data — HTML, CSS, JavaScript, and so on — and the simplest way to mainpulate this data is with a text editor. While HTML and CSS are @link["http://programmers.stackexchange.com/questions/28098/why-does-it-matter-that-html-and-css-are-not-programming-languages"]{not} programming languages, they lend themselves to semantic and logical structure that's most easily expressed by editing them as text. Furthermore, text-based editing makes debugging and performance improvements easier.
In one way, this makes sense. Web pages are made mostly of text-based data — HTML, CSS, JavaScript, and so on — and the simplest way to mainpulate this data is with a text editor. While HTML and CSS are not programming languages — you can't even compute 1 + 1 — they lend themselves to semantic and logical structure that's most easily expressed by editing them as text. Furthermore, text-based editing makes debugging and performance improvements easier.
But text-based editing is also limited. Though the underlying description of a web page is notionally human-readable, it's optimized to be readable by other software — namely, web browsers. HTML in particular is verbose and easily mistyped. And isn't it fatally dull to manage all the boilerplate, like surrounding every paragraph with @code{<p>...</p>}? Yes, it is.
But text-based editing is also limited. Though the underlying description of a web page is notionally human-readable, it's optimized to be readable by other software — namely, web browsers. HTML in particular is verbose and easily mistyped. And isn't it fatally dull to manage all the boilerplate, like surrounding every paragraph with @code{<p>...</p>}? Yes, it is.
For these reasons, much of web development should lend itself to @italic{abstraction} & @italic{automation}. Abstraction means consolidating repetitive, complex patterns into simpler, parameterized forms. Automation means avoiding the manual drudgery of generating the output files. But in practice, tools that enable this abstraction & automation have been slow to arrive, and most have come hobbled with unacceptable deficiencies.
For these reasons, much of web development should've lent itself to @italic{abstraction} & @italic{automation}. Abstraction means consolidating repetitive, complex patterns into simpler, more generalized forms. Automation means avoiding the drudgery and potential for error inherent in generating output files by hand. In other words, it starts to look a lot like programming.
@section{The better idea: a programming model}
@ -36,7 +34,13 @@ On the early web, the text-editing model was appealingly precise and quick. On s
What followed was a steady stream of products, frameworks, tools, and content management systems that claimed to bring a programming model to web development. Some were better than others. But none of them displaced the text editor as the preferred tool of web developers. And none of them matched the power and flexibility you get from any reasonable programming language.
Why not? These tools always promised a great leap forward in solving the web-development problem. In practice, however, they simply redistributed the pain. I needn't bore you by enumerating the deficiencies of specific tools, because they've tended to fail in the thematic ways:
Why not? These tools always promised a great leap forward in solving the web-development problem. In practice, however, they simply redistributed the pain.
Today, for instance, it's possible to find multiple preprocessors for @link["http://haml.info"]{HTML}, @link["http://sass-lang.com"]{CSS}, and @link["http://coffeescript.org"]{JavaScript}. But they're all separate tools, with different syntax and functions. Good luck finding a single preprocessor that can handle all your web files simultaneously.
This kind of thinking — from the edges inward, rather than from the center out — has been the thematic failure of web-publishing tools. Each is like the blind man of proverb with a single @link["http://www.jainworld.com/literature/story25.htm"]{hand on the elephant} — addressing a specific concern while missing the broader context.
Likewise, even web-publishing systems ostensibly based on general-purpose programming languages — like @link["https://wordpress.org"]{WordPress} or @link["https://www.djangoproject.com"]{Django} — suffer from recurring deficiencies:
@itemlist[
@ -59,7 +63,7 @@ In 2008, I launched a website called @link["http://typographyforlawyers.com"]{@i
So I used @link["http://wordpress.org"]{WordPress}. The major chore became scraping out all the crap that typically lives in blog templates. It ended up looking simpler & cleaner than the usual WordPress website. Largely because of this, people @link["http://ma.tt/2010/04/typography-for-lawyers/"]{liked it}.
Eventually, a publisher offered to release it as @link["http://typo.la/amzn"]{a paperback}, which came out in 2010.
Eventually, a publisher offered to release it as @link["http://typo.la/amzn"]{a paperback}, which came out in 2010 (the second edition was released in 2015).
Later came the inevitable request to make it into a Kindle book. As a fan of typography, I hate the Kindle. The layout controls are coarse, and so is the reading experience. But I didn't run and hide. Basically a Kindle book is a little website made with 1995-era HTML. So I coded up some tools in Perl to convert my book to Kindle format while preserving the formatting and images as well as possible.
@ -75,7 +79,7 @@ Did it work? Sort of. Source code went in; web pages came out. But it was also c
I had come across Racket while researching languages suitable for HTML/XML processing. I had unexpectedly learned about the @link["http://www.defmacro.org/ramblings/lisp.html"]{secret kinship} of XML and Lisp: though XML is not a programming language, it uses a variant of Lisp syntax. Thus Lisp languages are particularly adept at handling XMLish structures. That was interesting.
After comparing some of the Lisp & Scheme variants, @link["http://practicaltypography.com/why-racket-why-lisp.html"]{Racket stood out} because it had a text-based dialect called Scribble. Scribble could be used to embed code within textual content. That was interesting too. Among other things, this meant Scribble could be used as a general-purpose preprocessor. So I thought I'd see if I could add it to Pollen.
After comparing some of the Lisp & Scheme variants, @link["http://practicaltypography.com/why-racket-why-lisp.html"]{Racket stood out} because it had a text-based dialect called @seclink["getting-started" #:doc '(lib "scribblings/scribble/scribble.scrbl")]{Scribble}. Scribble could be used to embed code within textual content. That was interesting too. Among other things, this meant Scribble could be used as a @seclink["text" #:doc '(lib "scribblings/scribble/scribble-pp.scrbl")]{general-purpose preprocessor}. So I thought I'd see if I could add it to Pollen.
It worked. So well, in fact, that I started thinking about whether I could reimplement other parts of Pollen in Racket. Then I started thinking about reimplementing all of it in Racket.
@ -83,7 +87,7 @@ So I did. And here we are.
@section{What is Pollen?}
Pollen is a publishing system built on top of Scribble and Racket. So far, I've optimized Pollen for digital books, because that's mainly what I use it for. But it can be used for small projects too.
Pollen is a publishing system built on top of Scribble and Racket. So far, I've optimized Pollen for web-based books, because that's mainly what I use it for. But it can be used for small projects too, and non-webby things like @seclink["Adding_support_for_PDF_output"]{PDF}.
As a publishing system, Pollen includes:
@ -102,7 +106,7 @@ Pollen addresses the deficiencies I experienced with other tools:
@itemlist[
@item{@bold{Yes, we have a native data structure for HTML.} Racket represents HTML structures as @secref["X-expressions" #:doc '(lib "pollen/scribblings/pollen.scrbl")], which are a variant of the standard Racket data structure, called @italic{S-expressions}. In other words, not only is there a native representation for HTML, but it's represented the same way as everything else in the language.}
@item{@bold{Yes, we have a native data structure for HTML.} Racket represents HTML structures as @secref["X-expressions"], which are a variant of the standard Racket data structure, called @italic{S-expressions}. In other words, not only is there a native representation for HTML, but it's represented the same way as everything else in the language.}
@item{@bold{Flexible blending of code, presentation, and content.} Pollen is a text-based language. So a Pollen source file might have no code at all. But as a dialect of Scribble & Racket, if you want to mix code with content, you can.}

@ -1,6 +1,6 @@
#lang scribble/manual
@(require (for-label pollen/setup racket) "mb-tools.rkt")
@(require (for-label pollen/setup racket) "mb-tools.rkt" pollen/private/version)
@title[#:tag "first-tutorial"]{First tutorial: the project server & preprocessor}
@ -8,9 +8,9 @@ In this tutorial, you'll use Pollen to make a single HTML page with a poem. You'
@itemlist[
@item{The relationship of Racket & Pollen}
@item{The relationship of Pollen & Racket}
@item{DrRacket}
@item{DrRacket, the code editor}
@item{The project server}
@ -22,11 +22,11 @@ If you want the shortest possible introduction to Pollen, try the @secref["quick
@section[#:tag-prefix "tutorial-1"]{Prerequisites}
I'm going to assume that you've already installed Racket and Pollen. If not, do that now.
I'm going to assume that you've already @seclink["Installation"]{installed} Racket and Pollen. If not, do that now.
I'm also going to assume you know the basics of using a command line to run programs and navigate the file system using commands like @exec{cd} and @exec{ls}. On Mac OS X, your command-line program is called Terminal; on Windows it's the Windows Command Processor.
I'm also going to assume you know the basics of using a command line to run programs and navigate your file system using commands like @exec{cd} and @exec{ls}.
@section{The relationship of Racket & Pollen}
@section{Optional reading: the relationship of Racket & Pollen}
As I mentioned in the @secref["big-picture"], Pollen is built using Racket, and everything in Pollen ultimately becomes Racket code. If you're comfortable with that idea, you may move along.
@ -34,17 +34,15 @@ But if not, or if you're just a curious character:
One of the key features of Racket as a programming language is that it provides tools to create @italic{other} programming languages. These languages might look & behave @link["http://docs.racket-lang.org/ts-guide/index.html"]{like Racket}. Or they @link["http://hashcollision.org/brainfudge/"]{might not}. These languages might serve a general purpose, but more often they're specialized for a particular purpose, in which case they're known as @italic{domain-specific languages}, or @italic{DSLs}.
@margin-note{Racket exploits the fact that under the hood, all programming languages are basically doing the same thing. (CS jocks know this more formally as a side effect of @link["https://en.wikipedia.org/wiki/Turing_completeness"]{Turing completeness}.) Racket starts with the most general expression of a Turing-complete language — called @link["https://en.wikipedia.org/wiki/Lambda_calculus"]{the lambda calculus} — and lets users build on that. In most programming languages, you can build functions, classes, and modules. But in Racket, you can alter anything about the language.}
If you find this a strange idea, you're not alone. Most programmers — and until recently, me too — have never made or used DSLs. If you have a programming problem to solve, you start with a general-purpose language like Python or Java or Ruby, and go from there. Nothing wrong with that.
But programming languages contain their own design choices and compromises. Sometimes the problem at hand is best solved by manipulating the language at a deeper level. When you make a DSL, you're still programming in the underlying language, but doing so at a point of higher leverage.
Pollen is a DSL implemented in Racket. It is a close cousin of @other-doc['(lib "scribblings/scribble/scribble.scrbl")], another Racket DSL, which was designed for writing Racket documentation. The key feature of Scribble, and thus also of Pollen, is that it's text-based. Meaning, whereas most languages have source files made of code with text embedded within, Pollen's source files are text with code embedded within.
Moreover, Pollen is meant to be a small step away from Racket — you can think of it as a more convenient notation system for Racket code, similar to how Markdown is a more convenient notation for HTML. But unlike Markdown, which only lets you access a subset of HTML, anything that can be done in Racket can also be done in Pollen.
Moreover, Pollen is meant to be a small step away from Racket — you can think of it as a more convenient notation system for Racket code, similar to how Markdown is a more convenient notation for HTML. But unlike Markdown, which only lets you access a subset of HTML, anything that can be done in Racket can also be done in Pollen. (Also unlike Markdown, Pollen and Racket are full programming languages.)
As you work more with Pollen, you'll pick up more about how Pollen corresponds to Racket (see @secref["pollen-command-syntax"]) and easily be able to convert commands from one system to the other. In later tutorials, you'll see how larger Pollen projects are made out of both Pollen and Racket source files.
As you work with Pollen, you'll become familiar with how Pollen @seclink["pollen-command-syntax"]{corresponds to Racket}, and easily be able to convert commands from one notation to the other. In later tutorials, you'll see how larger Pollen projects can be assembled out of a mix of Pollen and Racket source files.
But in smaller projects, like this one, you can just use Pollen.
@ -52,7 +50,7 @@ But in smaller projects, like this one, you can just use Pollen.
DrRacket is the IDE for the Racket programming language, and other languages made with Racket (like Pollen). IDE stands for ``Integrated Development Environment,'' which is a fancy phrase for ``a nice place to edit and run your code.'' DrRacket is installed as part of the core Racket distribution.
@margin-note{If you've worked with languages like Perl, Python, or Ruby, you may be more familiar with using a general-purpose text editor to edit your code, and then running your program at the command line. You can do that with Racket too. But DrRacket is a considerately designed tool. I recommend it. For these tutorials, I'll assume you're using DrRacket. If you insist on using the command line, I trust you to figure out what you need to do to keep up.}
@margin-note{Experienced programmers may prefer to use a general-purpose text editor and the command line to edit and run Racket programs. That's fine. But for these tutorials, I'll assume you're using DrRacket. Otherwise, I trust you to figure out what you need to do to keep up.}
Launch DrRacket. Start a new file. The code in the file will look like this:
@ -63,7 +61,7 @@ Launch DrRacket. Start a new file. The code in the file will look like this:
Within the main window, you should also see an @defterm{interactions window}, which shows the output of the current file, and starts out looking something like this (details, like the version number, will vary):
@terminal{
Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].
Welcome to DrRacket, version @(version)--2015-11-26(-/f) [3m].
Language: racket; memory limit: 1000 MB.
> }
@ -85,7 +83,7 @@ When you start a new Pollen source file in DrRacket, you'll need to change the @
Now run your file by clicking the @onscreen["Run"] button in the upper-right corner, or select @menuitem["Racket" "Run"] from the menu. You'll get something like:
@terminal{
Welcome to DrRacket, version 6.0.1.6--2013-11-26(-/f) [3m].
Welcome to DrRacket, version @(version)--2015-11-26(-/f) [3m].
Language: pollen; memory limit: 1000 MB.
>
}
@ -139,9 +137,16 @@ This shows you something important: by default, any plain text in a Pollen sourc
File naming in Pollen is consequential.
Ultimately, every Pollen source file in your project will be @defterm{rendered} into an output file. Each Pollen source file corresponds to one output file. @bold{The name of this output file will be the name of the source file minus the Pollen source extension.} So a source file called @filepath{file.txt.pp} will become @filepath{file.txt}.
Ultimately, every Pollen source file in your project will be @seclink["Render"]{@defterm{rendered}} into an output file. Each Pollen source file corresponds to one output file. @bold{The name of this output file will be the name of the source file minus the Pollen source extension.} So a source file called @filepath{file.txt.pp} will become @filepath{file.txt}; @filepath{index.html.pp} will become @filepath{index.html}.
Thus, to derive the name of a source file, we
@itemlist[#:style 'ordered
@item{Take the name we want for the output file and}
@item{Append the appropriate Pollen file extension.}
]
Thus, to derive the name of a source file, we 1) take the name we want for the output file and 2) append the appropriate Pollen file extension. Different Pollen source files use different extensions — but more about that later. For now, the extension you'll use for your source is @filepath{.pp}.
Different Pollen source files use different extensions — but more about that later. For now, the extension you'll use for your source is @filepath{.pp}.
In this case, let's say we want to end up with a file called @filepath{poem.html}. Therefore, the name of our source file needs to be:
@ -149,9 +154,9 @@ the output name @filepath{poem.html}
@(linebreak)+ the source extension @filepath{.pp}
@(linebreak)= @filepath{poem.html.pp}
(If you want to name the file @filepath{something-else.html.pp}, be my guest. There's no special meaning associated with the prefix of a source file, only the suffixes.)
In the rest of this tutorial, if you want to name the file @filepath{something-else.html.pp}, be my guest. There's no special meaning associated with the prefix of a source file. Only the suffix.
@margin-note{If your system or text editor gives you grief for having two file extensions, you can use the underscore (@litchar{_}) to join the inner extension. So instead of @filepath{poem.html.pp}, the file would be named @filepath{poem_html.pp}. This filename will work exactly the same way, and still result in @filepath{poem.html} when rendered.}
@margin-note{If your system or text editor gives you grief for having two file extensions, you can alternatively use an underscore character (@litchar{_}) for the inner extension. So instead of @filepath{poem.html.pp}, your source file would be named @filepath{poem_html.pp}. This filename will work exactly the same way, and still result in @filepath{poem.html} when rendered.}
In a convenient location (e.g., your home directory or the desktop) create a new directory for your project called @code{tutorial}. In this new directory, save your DrRacket file as @filepath{poem.html.pp}.
@ -167,7 +172,7 @@ The border is too.
@section{Using the project server}
The project server is a web server built into Pollen. Just as DrRacket lets you run individual files and see if they work as you expect, the project server lets you preview and test your project as a real website. While working on your Pollen project, you may find it convenient to have DrRacket open on half your screen, and on the other half, a web browser pointing at the project server.
The project server is a web server built into Pollen. Just as DrRacket lets you run individual files and see if they work as you expect, the project server lets you preview and test your project through a web browser. While working on your Pollen project, you may find it convenient to have DrRacket open on half your screen, and on the other half, a web browser pointing at the project server.
@image/rp["project-server.png" #:scale 0.7]
@ -175,6 +180,8 @@ The project server is a web server built into Pollen. Just as DrRacket lets you
So use the project server.
@margin-note{Even if you're not planning to make web pages with Pollen, web browsers can display all common digital files, so the project server still ends up being a convenient previewing tool.}
A note about security. The project server isn't intended for real-world use, but rather as a development tool. That said, once you start the project server, it's an actual web server running on your machine, and it will respond to requests from any computer. If you want to limit traffic to your local network, or certain machines on your local network, it's your job — not mine — to configure your firewall or other network security measures accordingly.
@ -196,26 +203,29 @@ The first argument after @exec{raco} is the subcommand. For instance, @exec{raco
> raco pkg remove pollen
}
Likewise, @exec{raco pollen} lets you issue commands relevant to Pollen, like starting the project server. (See @secref["raco-pollen"] for a full description of available commands.)
Likewise, @exec{raco pollen} lets you issue commands relevant to Pollen. (See @secref["raco-pollen"] for a full listing.) Here's a simple one you can try:
Now we'll start the project server. Go to your command line and enter the following:
@terminal{
> raco pollen version
@pollen:version
}
Now we'll start the project server with @exec{raco pollen}. Go to your command line and enter the following:
@terminal{
> cd /path/to/tutorial
> raco pollen start}
@margin-note{Windows users, I'll trust you to convert @exec{raco} into the appropriate command for your system — assuming defaults, it's likely to be @exec{"C:\Program Files\Racket\raco"} (include the surrounding quotes in the command).}
After a moment, you'll see a startup message like this:
@terminal{
Welcome to Pollen 0.001 (Racket 6.x.x.x)
Welcome to Pollen @pollen:version (Racket @(version))
Project root is /path/to/tutorial/
Project server is http://localhost:8080 (Ctrl-C to exit)
Project dashboard is http://localhost:8080/index.ptree
Ready to rock}
@italic{Project root} means the directory that the project server was started in, and which it's treating as its root directory. Any absolute URLs (i.e., those beginning with @litchar{/}) will resolve into this directory. So a URL like @tt{/styles.css} will impliedly become @tt{/path/to/tutorial/styles.css}.
@italic{Project root} means the directory that the project server was started in, and which the server will regard as its root directory. Meaning, absolute URLs (i.e., those beginning with @litchar{/}) will resolve into this directory. So a URL like @tt{/styles.css} will impliedly become @tt{/path/to/tutorial/styles.css}.
If you use the bare command @exec{raco pollen start}, the project server will start in the current directory. But if you want to start the project server elsewhere, you can add that directory as an argument like this:
@ -223,9 +233,9 @@ If you use the bare command @exec{raco pollen start}, the project server will st
> raco pollen start /some/other/path
}
The next line of the startup message tells you that the web address of the project server is @tt{http://localhost:8080}. This is the address you put into your web browser to test your project. If you're unfamiliar with this style of URL, @tt{localhost} refers to your own machine, and @tt{8080} is the network port where the project server will respond to browser requests.
The next line of the startup message tells you that the web address of the project server is @tt{http://localhost:8080}. This is the address you'll put into your web browser to test your project. If you're unfamiliar with this style of URL, @tt{localhost} refers to your own machine, and @tt{8080} is the network port where the project server will respond to browser requests.
If you want to access the project server from a different machine, you can't use @tt{localhost}. But you can use the IP address of the machine running the project server (e.g., @tt{http://192.168.1.10:8080}) or any name for that machine available through local DNS (e.g., @tt{http://my-laptop:8080}).
If you want to run the project server on one machine and access it from a different machine (say, for testing purposes), you can't use @tt{localhost}. But you can use the IP address of the machine running the project server (e.g., @tt{http://192.168.1.10:8080}) or any name for that machine available through local DNS (e.g., @tt{http://my-laptop:8080}).
Though port @tt{8080} is the default, you can start the project server on any port you like by adding it as an argument to @exec{raco pollen start}:
@ -233,9 +243,6 @@ Though port @tt{8080} is the default, you can start the project server on any po
> raco pollen start /path/to/tutorial
> raco pollen start /path/to/tutorial 8088
}
@margin-note{You can also change the default port by altering @racket[default-project-server-port], or parameterizing it with @racket[current-server-port].}
Note that when you pass a port argument, you also have to pass a path argument. (Without it, you'll get an error, as illustrated below.) If you want the project server to start in the current directory, you can use the usual @litchar{.} shorthand:
@terminal{
@ -243,23 +250,23 @@ Note that when you pass a port argument, you also have to pass a path argument.
> raco pollen start 8088
@racketerror{/path/to/tutorial/8088 is not a directory}
> raco pollen start . 8088
Welcome to Pollen 0.001 (Racket 6.x.x.x) ...
Welcome to Pollen @pollen:version (Racket @(version)) ...
}
@margin-note{You can run multiple project servers simultaneously. Just start them on different ports so they don't conflict with each other.}
@margin-note{You can also change default port for the project server by overriding @racket[default-project-server-port], or parameterizing it with @racket[current-server-port]. You can also run multiple project servers simultaneously  just start them on different ports so they don't conflict with each other.}
Your terminal window will report status and error messages from the project server as it runs. Use @onscreen{Ctrl-C} to stop the server.
Your terminal window will report status and error messages from the project server as it runs. Use @exec{ctrl+C} to stop the server.
@subsection{Using the dashboard}
@subsection{The project dashboard}
For each directory in your project, starting at the top, the project server displays a @defterm{dashboard} in your web browser. The dashboard gives you an overview of the files in the directory, and links to view them.
The address of the top-level dashboard is @tt{http://localhost:8080/index.ptree}. Other dashboards follow the same pattern (e.g., @tt{http://localhost:8080/path/to/dir/index.ptree}.)
The address of the top-level dashboard is @link-tt{http://localhost:8080/index.ptree}. Other dashboards follow the same pattern (e.g., @tt{http://localhost:8080/path/to/dir/index.ptree}.)
Note that the dashboard is @bold{not} at @tt{http://localhost:8080/} or its equivalent, @tt{http://localhost:8080/index.html}. Why? So it doesnt interfere with any @tt{index.html} that you may want to put in your project.
@margin-note{The dashboard is deliberately @bold{not} at @tt{http://localhost:8080/} or its equivalent, @tt{http://localhost:8080/index.html}. Why? So it doesnt interfere with any @tt{index.html} that you may want to have in your project.}
Thus, the dashboard relies on a different file, called @filepath{index.ptree}. The @filepath{.ptree} extension is short for @defterm{pagetree}. In Pollen, a pagetree is a hierarchical list of pages. We'll do more with pagetrees in a later tutorial. For now, just be aware that to generate the dashboard, the project server will first look for an actual @filepath{index.ptree} file in each directory. If it doesn't find one, it will generate a pagetree from a listing of files in the directory.
Thus, the dashboard relies on a different file, called @filepath{index.ptree}. The @filepath{.ptree} extension is short for @defterm{pagetree}. In Pollen, a pagetree is a hierarchical list of pages. We'll do more with pagetrees in a later tutorial. For now, just be aware that to create a list of files for the dashboard, the project server will first look for an actual @filepath{index.ptree} file in each directory. If it doesn't find one, it will generate a pagetree from a listing of files in the directory.
Let's look at the root-level dashboard for our project. First, make sure your project server is running:
@ -268,7 +275,7 @@ Let's look at the root-level dashboard for our project. First, make sure your pr
> raco pollen start
}
Then, in your web browser, visit @link["http://localhost:8080/index.ptree"]{@tt{http://localhost:8080/index.ptree}}.
Then, in your web browser, visit @link-tt{http://localhost:8080/index.ptree}}.
You should see something like this:
@ -281,7 +288,7 @@ The top line tells us that we're in the root directory of the project. We didn't
We see the only file, @filepath{poem.html.pp}. Note that the @filepath{.pp} extension is grayed out. The dashboard automatically consolidates references to source and output files into a single entry. What this entry says is ``The directory contains a source file in @filepath{.pp} format for the output file @filepath{poem.html}.''
Every source-file entry in the dashboard has three links. The first link is attached to the filename itself, and takes you to a preview of the output file. If the output file doesn't yet exist — as is the case here — it will be dynamically rendered. (This is true whether you click its name in the dashboard, or link to it from another page.) So click the filename. You'll see in your web browser:
Every source file on the dashboard has three links. The first link is attached to the filename itself, and takes you to a preview of the output file. If the output file doesn't yet exist — as is the case here — it will be dynamically rendered when you click the link. (This is true whether you click its name in the dashboard, or link to it from another page.) So click the filename. You'll see in your browser:
@browser{
The margin is 42em. The border is red. The padding is 15em. The border is too.}
@ -335,39 +342,30 @@ For now, the files are identical except for the @tt{#lang} line. But let's chang
@section{Working with the preprocessor}
Pollen can operate in several processing modes. One of these is @defterm{preprocessor} mode. A preprocessor is a tool for making systematic, automated changes to a file, often in contemplation of further processing (hence the @defterm{pre-}). You can use the Pollen preprocessor this way. Or you can just use it on its own, and leave your files in a finished state.
Pollen can operate in several modes. One of these is @defterm{preprocessor} mode. A preprocessor is a tool for making systematic, automated changes to a file, often in contemplation of further processing (hence the @defterm{pre-}). You can use the Pollen preprocessor this way as the first step in a processing pipeline.
That's how we'll use it in this tutorial. We'll build out our @filepath{poem.html.pp} source file so that when it exits the preprocessor, we'll have a legit HTML file.
Or you can just use it on its own, and leave your files in a finished state. That's how we'll use it now. We'll build out our @filepath{poem.html.pp} source file so that when it exits the preprocessor, we'll have a legit HTML file.
@subsection{Setting up a preprocessor source file}
The file extension of a Pollen source file tells Pollen what kind of processing to apply to it. The @filepath{.pp} file extension stands for ``Pollen preprocessor.'' You can use the preprocessor with any text-based file by:
@itemlist[
@margin-note{For more about the Pollen processing modes, see @secref["file-types"].}
@item{inserting @tt{#lang pollen} as the first line,}
@subsection{Setting up a preprocessor source file}
@item{adding the @filepath{.pp} file extension,}
The file extension of a Pollen source file tells Pollen what kind of processing to apply to it. The @filepath{.pp} file extension stands for ``Pollen preprocessor.'' Here's how you can use the preprocessor with any text-based file:
@item{running it through Pollen.}
]
@itemlist[#:style 'ordered
@margin-note{For more about the Pollen processing modes and how to invoke them, see @secref["file-types"].}
@item{Insert @tt{#lang pollen} as the first line.}
``I can use the preprocessor with @bold{any} kind of text-based file?'' Right. ``But how?'' The preprocessor reads the source file, handles any Pollen commands it finds, and lets the rest of the content pass through untouched. To the preprocessor, it's all just text data. It doesn't care whether that text represents HTML, CSS, JavaScript, or even @link["https://en.wikipedia.org/wiki/TI-BASIC"]{TI-BASIC}.
@item{Add the @filepath{.pp} file extension.}
One caveat: because the preprocessor only deals with text, the Pollen commands you use in the preprocessor also have to produce text. Moreover, Pollen doesn't enforce the syntax rules of the underlying file — that's your responsibility. For instance, Pollen won't stop you from doing nonsensical things like this:
@item{Run it through Pollen.}
]
@fileblock["bad-poem.html.pp" @codeblock{
#lang pollen
The cave is pitch black.
Look out for the grue.
◊(insert-mp3-recording-of-scream)
}]
Pollen will fulfill your request, but the result won't be valid HTML, because you can't simply drop binary data in the middle of an HTML file. As Mr. Babbage warned — garbage in, garbage out.
``I can use the preprocessor with @bold{any} kind of text-based file?'' Right. ``But how?'' The preprocessor reads the file, handles any Pollen commands it finds, and lets the rest of the content pass through untouched. To the preprocessor, it's all just text data. It doesn't care whether that text represents HTML, CSS, XML, LaTeX, or even @link["https://en.wikipedia.org/wiki/TI-BASIC"]{TI-BASIC}.
I've encouraged you to mess with the source file, but let's return it to its original state:
Let's verify that our source file is ready for the preprocessor:
@fileblock["/path/to/tutorial/poem.html.pp" @codeblock{
#lang pollen
@ -378,7 +376,7 @@ The padding is 15em.
The border is too.
}]
This file has @tt{#lang pollen} as the first line, and @filepath{.pp} as the file extension, so it meets the minimum requirements for the preprocessor.
This file has @tt{#lang pollen} as the first line, and @filepath{.pp} as the file extension. So yes — it meets the minimum requirements for the preprocessor.
@subsection{Creating valid HTML output}
@ -398,7 +396,7 @@ The border is too.
</body>
</html>}]
Return to the project server and view @link["http://localhost:8080/poem.html" "http://localhost:8080/poem.html"]. Earlier, the output looked like this:
Return to the project server and view @link-tt{http://localhost:8080/poem.html}. Earlier, the output looked like this:
@browser{
The margin is 42em. The border is red. The padding is 15em. The border is too.}
@ -416,36 +414,70 @@ As before, because the source has changed, Pollen refreshes the output file. Fro
This is now a valid HTML page.
@subsection{Adding commands}
@subsection{Adding Pollen commands}
I mentioned that the preprocessor reads the file and handles any Pollen commands it finds. But our source file doesn't have any commands yet. Let's add some.
I mentioned that the preprocessor reads the file and handles any Pollen commands it finds. But our source file doesn't have any yet.
Pollen commands can be embedded in your source file using one of two modes: @defterm{Racket mode} or @defterm{Pollen mode}. We'll try Pollen mode in a later tutorial. For now, we'll use Racket mode.
Let's add some. Pollen commands in your source file can be written one of two ways: @defterm{Pollen style} or @defterm{Racket style}. We'll try Pollen-style commands in the next tutorial. For now, we'll use Racket style.
To make a Racket-mode command, just take any Racket expression and put the lozenge character @litchar["◊"] in front of it. For instance, these are valid Racket expressions:
To make a Racket-style command, just take any Racket command and put the lozenge character @litchar["◊"] in front of it. For instance, these are valid Racket commands:
@codeblock{
#lang racket
(define inner 2)
(define edge (* inner 4))
(define inside 2)
(define edge (* inside 4))
(define color "blue")
}
And these are the equivalent commands in Pollen:
And these are the equivalent Racket-style commands in Pollen:
@codeblock{
#lang pollen
◊(define inner 2)
◊(define edge (* inner 4))
◊(define inside 2)
◊(define edge (* inside 4))
◊(define color "blue")
}
@margin-note{How to type a lozenge:
@(linebreak)@bold{Mac}: option + shift + V
@(linebreak)@bold{Windows}: holding down alt, type 9674 on the num pad
@(linebreak)@bold{Ubuntu}: ctrl + shift + U, then 25CA}
Couldn't be simpler.
One caveat: because the preprocessor only deals with text, the Pollen commands you use in the preprocessor have to produce values that are either text, or can be sensibly converted to text. The preprocessor helps you out by automatically converting basic Racket values to text:
@fileblock["odd-values.html.pp" @codeblock{
#lang pollen
◊(void) ; this will become an empty string
◊(string->path "foo")
◊#\C
◊sqrt
◊(sqrt 2)
◊(list 1 2 3 4 5)
◊(map sqrt (list 1 2 3 4 5))
}]
@repl-output{
foo
C
#<procedure:sqrt>
1.4142135623730951
'(1 2 3 4 5)
'(1 1.4142135623730951 1.7320508075688772 2 2.23606797749979)}
If you're using more complicated data types, or you don't like the preprocessor's automatic conversion, you can convert your values to text by hand.
That said, Pollen won't stop you from doing nonsensical things like this:
@fileblock["bad-poem.html.pp" @codeblock{
#lang pollen
The cave is pitch black.
Look out for the grue.
◊(insert-mp3-recording-of-scream)
}]
Pollen will try to fulfill your request, but weird things could happen. And the result definitely won't be valid HTML, because you can't simply drop binary data in the middle of an HTML file.
As Mr. Babbage warned — garbage in, garbage out.
@subsection{Racket basics (if you're not familiar)}
@ -455,34 +487,34 @@ Couldn't be simpler.
@item{The core building block of Racket is the @italic{expression}. An expression can be a single value (like @racket[2] or @racket{blue}), a variable (like @code{edge}), a list of values (like @racket[(list 2 "blue" edge)]), or a function call.}
@item{Function calls go between parentheses. Unlike most languages, the function name comes @italic{first}, followed by its arguments (so it's @racket[(* inner 4)], not @racket[(inner * 4)]). This is called @italic{prefix notation}.}
@item{Function calls go between parentheses. Unlike most languages, the function name comes @italic{first}, followed by its arguments (so it's @racket[(* inside 4)], not @racket[(inside * 4)]). This is called @italic{prefix notation}.}
@item{Every expression is @italic{evaluated} to produce a value. A variable evaluates to whatever value it holds (so after we say @code{(define inner 2)}, @code{inner} would evaluate to @racket[2]). A function call evaluates to its return value (so @racket[(+ 2 2)] would evaluate to @racket[4]).}
@item{Every expression is @italic{evaluated} to produce a value. A variable evaluates to whatever value it holds (so after we say @code{(define inside 2)}, @code{inside} would evaluate to @racket[2]). A function call evaluates to its return value (so @racket[(+ 2 2)] would evaluate to @racket[4]).}
@item{Expressions can contain recursively nested expressions. Thus, @racket[(* inner 4)] could be written @racket[(* inner (+ 2 2))] or @racket[(* inner (+ (+ 1 1) (+ 1 1)))].}
@item{Expressions can contain recursively nested expressions. Thus, @racket[(* inside 4)] could be written @racket[(* inside (+ 2 2))] or @racket[(* inside (+ (+ 1 1) (+ 1 1)))].}
]
@margin-note{Newcomers to Racket often gripe about prefix notation and parentheses. If you need to get it out of your system, go ahead. Keep in mind, however, that it's not some peculiar affectation, but rather a necessary consequence of rule #1. As you'll come to learn, rule #1 is where the magic happens.}
@margin-note{Newcomers to Racket often gripe about prefix notation and parentheses. If you need to get it out of your system, go ahead. Keep in mind, however, that it's not some peculiar affectation, but rather a necessary consequence of rule #1. In time, you'll agree that the benefits outweigh the costs.}
That's all you need to figure out what's going on in the Pollen commands below:
@codeblock{
#lang pollen
◊(define inner 2)
◊; create a variable 'inner' that holds the value 2
◊(define edge (* inner 4))
◊; create a variable 'edge' that's four times the value of 'inner'
◊(define inside 2)
◊; create a variable `inside` that holds the value 2
◊(define edge (* inside 4))
◊; create a variable `edge` that's four times the value of `inside`
◊(define color "blue")
◊; create a variable 'color' that holds the value "blue"
◊; create a variable `color` that holds the value "blue"
}
To learn more about Racket syntax, consider a detour through the excellent @other-doc['(lib "scribblings/quick/quick.scrbl")].
@subsection{Defining variables with commands}
@subsection{Defining variables with Racket-style commands}
Let's use commands to define variables that will hold some values for our page. First, add a @code{<head>} tag to your source file, and three commmands to define three variables:
Let's use Racket-style commands to define variables that will hold some values for our page. First, add a @code{<head>} tag to your source file, and three commmands to define three variables:
@fileblock["/path/to/tutorial/poem.html.pp" @codeblock{
#lang pollen
@ -490,8 +522,8 @@ Let's use commands to define variables that will hold some values for our page.
<!DOCTYPE html>
<html>
<head>
◊(define inner 2)
◊(define edge (* inner 4))
◊(define inside 2)
◊(define edge (* inside 4))
◊(define color "blue")
</head>
<body>
@ -504,7 +536,7 @@ The border is too.
</body>
</html>}]
Then look at @link["http://localhost:8080/poem.html" "http://localhost:8080/poem.html"] again. Does it look the same? Not a trick question — it should. If you click the @onscreen{Out} link on the dashboard, you'll see this:
Then look at @link-tt{http://localhost:8080/poem.html}} again. Does it look the same? Not a trick question — it should. If you click the @onscreen{Out} link on the dashboard, you'll see this:
@terminal{
<!DOCTYPE html>
@ -524,7 +556,7 @@ The border is too.
</body>
</html>}
What's with the blank lines? Don't panic — our @tt{◊(define ...)} commands create variables, so they don't evaluate to any value. Instead, we get blank lines. (Don't panic about that either — @racket[define] is a rare exception to the general rule that all expressions evaluate.) So far, so good.
What's with the blank lines? Don't panic — a @tt{◊@racket[(define ...)]} command creates a variable, but the command itself doesn't evaluate to any value. So instead, we get blank lines. (Don't panic about that either — @racket[define] is an exception to the general rule that all expressions evaluate.) So far, so good.
@subsection{Inserting values from variables}
@ -536,21 +568,21 @@ To insert the value of a variable in our file, we use the command @litchar{◊|}
<!DOCTYPE html>
<html>
<head>
◊(define inner 2)
◊(define edge (* inner 4))
◊(define inside 2)
◊(define edge (* inside 4))
◊(define color "blue")
</head>
<body>
<pre>
The margin is ◊|edge|em.
The border is ◊|color|.
The padding is ◊|inner|em.
The padding is ◊|inside|em.
The border is too.
</pre>
</body>
</html>}]
Here, we're replacing three values in the poem with the variables containing those values — @tt{◊|edge|}, @tt{◊|color|}, and @tt{◊|inner|}. @link["http://localhost:8080/poem.html"]{Reload the file} in the project server, and you'll see:
Here, we're replacing three values in the poem with the variables containing those values — @code{◊|edge|}, @code{◊|color|}, and @code{◊|inside|}. @link["http://localhost:8080/poem.html"]{Reload the file} in the project server, and you'll see:
@terminal{
The margin is 8em.
@ -560,11 +592,13 @@ The border is too.}
Hey, look at that — the text of the poem changed. Now it even rhymes.
If you like, in the source file, edit the variable definitions with different values and reload the page in the project server. The page will be rendered afresh with the new values. In particular, if you update @code{inner}, you'll also see @code{edge} change, since its value depends on @code{inner}.
If you like, in the source file, edit the variable definitions with different values and reload the page in the project server. The page will be rendered afresh with the new values. In particular, if you update @racket[inside], you'll also see @racket[edge] change, since its value depends on @racket[inside].
@margin-note{Pro tip: when inserting a variable as a value, you can also just type @racket[◊variable-name]. The vertical bars on the sides are never wrong, but only mandatory when you want the value to appear flush against other text on the right.}
@subsection{Inserting variables within CSS}
Our poem makes claims about the @code{margin}, @code{border}, and @code{padding} of the page that aren't yet true. To fix this, we'll rely on the same basic technique of inserting variables into our HTML file. But instead of putting them in the @code{<body>} of the page, we'll put them in a CSS @code{<style>} tag.
Our poem now makes claims about the @code{margin}, @code{border}, and @code{padding} of the page that aren't yet true. To fix this, we'll rely on the same basic technique of inserting variables into our HTML file. But instead of putting them in the @code{<body>} of the page, we'll put them in a CSS @code{<style>} tag.
Update the @code{<head>} section of the page with a new @code{<style>} tag that defines a style for @code{pre} like so, using our variables for the relevant values:
@ -576,14 +610,14 @@ Update the @code{<head>} section of the page with a new @code{<style>} tag that
<!DOCTYPE html>
<html>
<head>
◊(define inner 2)
◊(define edge (* inner 4))
◊(define inside 2)
◊(define edge (* inside 4))
◊(define color "blue")
<style type="text/css">
pre {
margin: ◊|edge|em;
border: ◊|inner|em solid ◊|color|;
padding: ◊|inner|em;
border: ◊|inside|em solid ◊|color|;
padding: ◊|inside|em;
}
</style>
</head>
@ -591,7 +625,7 @@ pre {
<pre>
The margin is ◊|edge|em.
The border is ◊|color|.
The padding is ◊|inner|em.
The padding is ◊|inside|em.
The border is too.
</pre>
</body>
@ -600,20 +634,20 @@ The border is too.
Notice that we're using the same @litchar{◊|}@italic{variable-name}@litchar{|} pattern as before to insert the variable values.
What do we expect to see? We expect that the @code{padding} and @code{border} will be 2em wide, because @code{inner} is 2. We expect the @code{margin} to be 8em, because it's equal to @code{edge}, which is @code{inner} multiplied by 4. And we expect the color of the border to be @racket["blue"], because that's the value of the variable @code{color}.
What do we expect to see? We expect that the @code{padding} and @code{border} will be @code{2em} wide, because @code{inside} is @code{2}. We expect the @code{margin} to be @code{8em}, because it's equal to @code{edge}, which is @code{inside} multiplied by @code{4}. And we expect the color of the border to be @racket["blue"], because that's the value of the variable @code{color}.
And indeed, when you @link["http://localhost:8080/poem.html"]{reload the file} in the project server, you'll see exactly that:
@image/rp["result.png" #:scale 0.7]
As before, if you edit the values of the variables in the source file and reload in the project server, you'll see both the text and the layout change.
As before, if you edit the values of the variables in the source file and go back to the project server, you'll see both the text and the layout change.
@section{First tutorial complete}
This was a sneaky tutorial. The HTML page we made was very simple, but in building it, we covered many important points about how Pollen works.
This was a sneaky tutorial. Though the HTML page we made was very simple, we saw all the key elements of the Pollen development environment, and learned about the preprocessor.
Feel free to go back and experiment with what you've learned. The next tutorial will assume that you're comfortable with all the material here.
Feel free to go back and experiment with what you've learned. The @seclink["second-tutorial"]{next tutorial} will assume that you're comfortable with all the material here.

@ -1,6 +1,6 @@
#lang scribble/manual
@(require scribble/eval racket/date (for-label pollen/core racket/file racket/system pollen/decode plot pollen/setup pollen/tag racket/base pollen/template txexpr racket/list racket/string pollen/render))
@(require scribble/eval racket/date (for-label racket/date pollen/core racket/file racket/system pollen/decode plot pollen/setup pollen/tag racket/base pollen/template txexpr racket/list racket/string pollen/render))
@(require "mb-tools.rkt")
@(define my-eval (make-base-eval))
@ -33,11 +33,11 @@ I'll assume you've completed the @seclink["third-tutorial"]{third tutorial} and
I'll also assume that you're comfortable with @seclink["Attaching_behavior_to_tags"] with tag functions, and that you can read and write basic Racket functions. Most of this tutorial is programming — easy programming, but programming nonetheless.
@section{Multiple-output publishing and its discontents}
@section{Optional reading: Multiple-output publishing and its discontents}
Publishing documents in multiple output formats is a common need. A common solution is to write or render your document in one output format, and then convert to others as needed. And, for simple documents, this can work well enough.
But in general, writing your document directly in an output format, like Markdown or HTML, is a bad idea. Why? Because output formats are just that — output formats. They're optimized to store the kind of information that the output device needs, not the information that the writer might want. Thus, using them as input formats means losing a huge amount of expressivity. I discussed this issue in @seclink["The_case_against_Markdown"]. Markdown is (too) often valorized as an authoring format, but it's not expressive or semantic. It's just a way of notating HTML, which is merely a boring and limited output format.
But in general, writing your document directly in an output format, like Markdown or HTML, is a bad idea. Why? Because output formats are just that — output formats. They're optimized to store the kind of information that the output device needs, not the information that the writer might want. Thus, using them as input formats means losing a huge amount of expressivity. I discussed this issue in @seclink["the-case-against-markdown"]{The case against Markdown}. Markdown is (too) often valorized as an authoring format, but it's not expressive or semantic. It's just a way of notating HTML, which is merely a boring and limited output format.
Converting a document from one input format to another is better — at least you get the benefit of using a more expressive input format. The problem is that richness doesn't necessarily carry through as you convert between formats, which involves simplifying assumptions about how certain entities map to each other. Again, that's not a knock on document converters like @link["http://pandoc.org"]{Pandoc} — if your document is simple enough, and you're satisfied with the assumptions made during the conversion process, great.
@ -83,7 +83,7 @@ In the previous tutorials, you saw how Pollen source files correspond to certain
In a multiple-output project, a source file no longer has a one-to-one correspondence with a specific output type. To indicate this, we'll instead use the special @tt{poly} extension. So our @filepath{document.html.pm} will become @filepath{document.poly.pm}.
@margin-note{The @tt{poly} extension is the default, but can be changed for a project by using the @racket[setup:poly-source-ext] setting.}
@margin-note{The @tt{poly} extension is the default, but can be changed for a project by overriding the @racket[default-poly-source-ext] setting.}
Let's set up a new multi-output project for a résumé. Find a convenient directory and create a new @tt{poly} source file as follows:
@ -115,17 +115,17 @@ This proves that our source file is working. It looks dumb, however, because we
@fileblock["pollen.rkt" @codeblock|{
#lang racket/base
(require racket/date)
(require racket/date txexpr)
(provide (all-defined-out))
(define (get-date)
(date->string (current-date)))
(define (heading . xs)
`(h2 ,@xs))
(define (emph . xs)
`(strong ,@xs))
(define (heading . elements)
(txexpr 'h2 empty elements))
(define (emph . elements)
(txexpr 'strong empty elements))
}|]
The @racket[get-date] tag function will insert the current date as a string. The @racket[heading] and @racket[emph] tag functions will become typical HTML @racket[h2] and @racket[strong] tags respectively. (If it's unclear why this is so, this would be a good time to review @seclink["Using_Racket_s_function_libraries"] and @seclink["Returning_an_X-expression"].)
@ -140,19 +140,19 @@ Today is @(date->string (current-date)). I @bold{really} want this job.
@subsection{Adding output targets for @tt{poly} sources}
Though Pollen imputes HTML as a target for poly sources by default, if you only wanted HTML, you wouldn't be using a poly source. So our next step will be to explicitly define the output targets that we want to associate with poly sources.
Though Pollen uses HTML as the default target for poly sources, if you only wanted HTML, you wouldn't be using a poly source. So our next step will be to explicitly define the output targets that we want to associate with poly sources.
@subsubsection{Using the @tt{setup} submodule}
We'll do this by overriding Pollen's @racket[default-poly-targets] value from within our @filepath{pollen.rkt}.
We'll do this by setting the @racket[setup:poly-targets] value in our @filepath{pollen.rkt}. If you haven't investigated it yet, the @racket[pollen/setup] module offers @seclink["setup-overrides"] that allow you to configure certain Pollen characteristics from within a @filepath{pollen.rkt} file. The example on that page, for instance, shows how to change the markup source extension and the Pollen command character.
@subsubsection{Using the @tt{setup} submodule}
The idea is that you add a @racket[setup] submodule to your @filepath{pollen.rkt} file with a @racket[define] statement for the value. Because we're defining the local value, we drop the @racket[setup:] prefix and just call it @racket[poly-targets]. Our value will be a list of file extensions denoting the targets. To start, let's set our output formats to HTML and plain text, which we'll denote with the list of extensions @racket['(html txt)].
If you haven't investigated it yet, @racketmodname[pollen/setup] offers @seclink["setup-overrides"]{overridable values} that allow you to configure certain Pollen characteristics from within your @filepath{pollen.rkt} file. The example on that page, for instance, shows how to change the markup source extension and the Pollen command character.
@margin-note{I'm glossing over the details of @seclink["submodules" #:doc '(lib "scribblings/guide/guide.scrbl")], but they're one of the best-considered features of the Racket language. What makes submodules so handy is that they are truly independent: you can load a submodule from a source file without running the main body of the file. Thus, tasks like this — setting configuration values — that might require separate files in other languages can be handled as submodules in Racket.}
The idea is that you add a @racket[setup] submodule to your @filepath{pollen.rkt} file with a @racket[define] statement for the value you want to override. Because we're defining an override value, we drop the @racket[default-] prefix and just call it @racket[poly-targets]. Our value will be a list of file extensions denoting the targets. To start, let's set our output formats to HTML and plain text, which we'll denote with the list of extensions @racket['(html txt)].
@fileblock["pollen.rkt" @codeblock|{
#lang racket/base
(require racket/date)
(require racket/date txexpr)
(provide (all-defined-out))
(module setup racket/base
@ -162,18 +162,18 @@ The idea is that you add a @racket[setup] submodule to your @filepath{pollen.rkt
(define (get-date)
(date->string (current-date)))
(define (heading . xs)
`(h2 ,@xs))
(define (heading . elements)
(txexpr 'h2 empty elements))
(define (emph . xs)
`(strong ,@xs))
(define (emph . elements)
(txexpr 'strong empty elements))
}|]
Though you ordinarily don't have to restart the project server to see changes in @filepath{pollen.rkt}, you do for @racket[config] values, because they're stashed in a submodule. On restart, the project server will look like this:
@image/rp["poly-ps-html-txt.png" #:scale 0.45]
What's happened is that @racket[setup:poly-targets] now reflects the settings in @filepath{pollen.rkt}. The project server sees that we want to associate poly files with HTML and plain-text targets, and accordingly shows us two entries in the project-server listing: @filepath{cv.html.pm} and @filepath{cv.txt.pm}. As the adjacent message indicates, these are not new source files on disk, but rather implied by @filepath{cv.poly.pm}.
What's happened is that the project server now knows about our @racket[setup] submodule in @filepath{pollen.rkt}. It sees that we want to associate poly source files with HTML and plain-text targets, and accordingly shows us two entries in the project-server listing: @filepath{cv.html.pm} and @filepath{cv.txt.pm}. As the adjacent messages indicate, these are not new source files on disk, but are rather derived from @filepath{cv.poly.pm}.
If you click on @filepath{cv.html.pm}, you'll see the same HTML output that you saw before. If you click on @filepath{cv.txt.pm}, however, you'll see this:
@ -186,21 +186,26 @@ If you click on @filepath{cv.html.pm}, you'll see the same HTML output that you
Don't panic. What we're seeing is the X-expression generated from the @filepath{cv.poly.pm} file, but formatted as plain text rather than HTML. It looks wrong because we haven't updated our project to handle plain-text output.
@margin-note{I'm glossing over the details of @seclink["submodules" #:doc '(lib "scribblings/guide/guide.scrbl")], but they're one of Racket's best features. What makes them useful is that they're truly independent: you can load a submodule from a source file @italic{without} running the main body of the file. Thus, tasks like this — setting configuration values — that would require separate files in other languages can be handled as submodules in Racket.}
@subsection{Adding support for another output format}
The goal of this whole endeavor is to derive multiple output files from one source file. Thus, to make our résumé look right in plain text, we won't change anything in the source file. But we will add a template and update our tag functions.
The goal of this whole endeavor was to derive multiple output files from one source file. Thus, to make our résumé look right in plain text, we won't change anything in the source file. But we will add a template and update our tag functions.
@subsubsection{Adding a template for @tt{.txt}}
@seclink["Templates" #:tag-prefixes '("tutorial-2")] should be familiar to you by now. As usual, the name of the template is @tt{template} plus the relevant file extension, so in this case @filepath{template.txt}. Add the file as follows:
@seclink["Templates" #:tag-prefixes '("tutorial-2")] should be familiar to you by now. As usual, the name of the template is @tt{template} plus the relevant file extension, so in this case @filepath{template.txt.p}. Add the file as follows:
@fileblock["template.txt" @codeblock|{
@fileblock["template.txt.p" @codeblock|{
◊(local-require racket/list)
◊(apply string-append (filter string? (flatten doc)))
}|]
What we're doing here is converting the X-expression to text in a smarter way. We use @racket[local-require] to bring in @racket[racket/list] so we can use the @racket[flatten] function. Then, to understand what the next line does, just read it from the inside out: ``Take the @racket[doc] export from the source file (which is an X-expression), @racket[flatten] it into a list, @racket[filter] with @racket[string?] (creating a list that's only strings) and @racket[apply] the @racket[string-append] function to these, resulting in one big string.'' Which is exactly what we need for a plain-text file.
What we're doing here is converting the X-expression to text in a smarter way. Because we're in a template, we use @racket[local-require] (rather than plain @racket[require]) to bring in @racketmodname[racket/list] so we can use the @racket[flatten] function.
To understand what the next line does, just read it from the inside out: ``Take the @racket[doc] export from the source file (which is an X-expression), @racket[flatten] it into a list, @racket[filter] with @racket[string?] (creating a list that's only strings) and @racket[apply] the @racket[string-append] function to these, resulting in one big string.'' Which is exactly what we need for a plain-text file.
When you return to the project server and click on @filepath{cv.txt.pm}, you'll see the result:
@ -211,7 +216,7 @@ Today is @(date->string (current-date)). I really want this job.}
So far, so good. We've got legible plain text. But we've completely lost our formatting. Let's fix that.
@margin-note{Have you ever used @code{◊(define-meta template ...)} to specify a template for a particular source file? You can still use this in a multiple-output project — just supply a list of templates rather than a single template. See @racket[get-template-for].}
@margin-note{Have you ever used @code{◊(define-meta template ...)} to specify a template from within a source file? You can still use this in a multiple-output project — just supply a list of templates rather than a single template. See @racket[get-template-for].}
@subsubsection{Branching tag functions}
@ -221,11 +226,11 @@ But plain text doesn't have @racket[h2] or @racket[strong]. So how about this: w
``So how do we make our tags mean one thing for HTML and a different thing for plain text?'' We make @italic{branching tag functions} that do different things depending on what the current rendering target for poly sources is.
That value, in fact, is stored in a Pollen @seclink["parameterize" #:doc '(lib "scribblings/guide/guide.scrbl")]{parameter} called @racket[(current-poly-target)]. What we're going to do is rewrite our tag functions to behave differently based on the value of this parameter. Update your @filepath{pollen.rkt} as follows:
That value, in fact, is stored in a Pollen @seclink["parameterize" #:doc '(lib "scribblings/guide/guide.scrbl")]{parameter} called @racket[current-poly-target]. What we're going to do is rewrite our tag functions to behave differently based on the value of this parameter. Update your @filepath{pollen.rkt} as follows:
@fileblock["pollen.rkt" @codeblock|{
#lang racket/base
(require racket/date pollen/setup)
(require racket/date txexpr pollen/setup)
(provide (all-defined-out))
(module setup racket/base
@ -235,20 +240,20 @@ That value, in fact, is stored in a Pollen @seclink["parameterize" #:doc '(lib "
(define (get-date)
(date->string (current-date)))
(define (heading . xs)
(define (heading . elements)
(case (current-poly-target)
[(txt) (map string-upcase xs)]
[else `(h2 ,@xs)]))
[(txt) (map string-upcase elements)]
[else (txexpr 'h2 empty elements)]))
(define (emph . xs)
(define (emph . elements)
(case (current-poly-target)
[(txt) `("**" ,@xs "**")]
[else `(strong ,@xs)]))
[(txt) `("**" ,@elements "**")]
[else (txexpr 'strong empty elements)]))
}|]
Here, I've chosen to use @racket[case] because it's compact. But you can use any conditional structure you want (@racket[cond] would be another obvious choice). You can see that in the tag functions for @racket[heading] and @racket[emph], we've added a branch for the @racket[txt] output format. As promised, for @racket[heading] we're capitalizing the text, and in @racket[emph] we're adding double asterisks.
Here, we see that the @code{heading} and @code{emph} tag functions have been extended with branching behavior. I've chosen to use @racket[case] because it's compact. But you can use any branching structure you want (@racket[cond] would be another obvious choice). In both places, we've added a branch for the @racket[txt] output format. As promised, for @racket[heading] we're capitalizing the text, and in @racket[emph] we're adding double asterisks.
@margin-note{Could you use @racket[(html)] rather than @racket[else] for the second case? Sure. Should you? It's good practice to write conditionals with an @racket[else] because it guarantees that there's always a result. If @racket[case] (or @racket[cond]) doesn't find a matching clause, it returns @racket[void], which can be surprising or annoying. But do what you like. I'm not the @racket[else] police.}
@margin-note{Could you use @racket[(html)] rather than @racket[else] for the second case in each tag function? Sure. Should you? It's good practice to write branching conditionals with an @racket[else] because it guarantees that there's always a result. If @racket[case] (or @racket[cond]) doesn't find a matching clause, it returns @racket[void], which can be surprising or annoying. But do what you like. I'm not the @racket[else] police.}
Now when we return to the project server and refresh @filepath{cv.txt.pm}, we see our groovy plain-text formatting:
@ -263,13 +268,13 @@ By the way, the reason I included @racket[get-date] in this tutorial is to illus
@subsection{Adding support for LaTeX output}
To add more output formats, we just repeat the same fandango: add a rendering target to our @racket[config] submodule, update any branching tag functions, and add a template for the new format.
To add more output formats, we just repeat the same fandango: add a new rendering target to our @racket[setup] submodule, update any branching tag functions, and add a template for the new format.
Let's see how fast we can add support for LaTeX output. Here's the updated @filepath{pollen.rkt}:
@fileblock["pollen.rkt" @codeblock|{
#lang racket/base
(require racket/date pollen/setup)
(require racket/date txexpr pollen/setup)
(provide (all-defined-out))
(module setup racket/base
@ -279,24 +284,24 @@ Let's see how fast we can add support for LaTeX output. Here's the updated @file
(define (get-date)
(date->string (current-date)))
(define (heading . xs)
(define (heading . elements)
(case (current-poly-target)
[(ltx) (apply string-append `("{\\huge " ,@xs "}"))]
[(txt) (map string-upcase xs)]
[else `(h2 ,@xs)]))
[(ltx) (apply string-append `("{\\huge " ,@elements "}"))]
[(txt) (map string-upcase elements)]
[else (txexpr 'h2 empty elements)]))
(define (emph . xs)
(define (emph . elements)
(case (current-poly-target)
[(ltx) (apply string-append `("{\\bf " ,@xs "}"))]
[(txt) `("**" ,@xs "**")]
[else `(strong ,@xs)]))
[(ltx) (apply string-append `("{\\bf " ,@elements "}"))]
[(txt) `("**" ,@elements "**")]
[else (txexpr 'strong empty elements)]))
}|]
Notice that we added a @racket[ltx] extension to the list of @racket[poly-targets]. We also updated @racket[heading] and @racket[emph] to use comparable LaTeX commands.
Then a @filepath{template.ltx}:
Then a @filepath{template.ltx.p}:
@fileblock["template.ltx" @codeblock|{
@fileblock["template.ltx.p" @codeblock|{
\documentclass[a4paper,12pt]{letter}
\begin{document}
◊(local-require racket/list)
@ -304,7 +309,7 @@ Then a @filepath{template.ltx}:
\end{document}
}|]
Here, all we did was take our @filepath{template.txt} (which turned an X-expression into a string) and wrap it in the bare minimum LaTeX boilerplate. (Confidential to LaTeX fans: please don't write to complain about my rudimentary LaTeX. It's a tutorial. I trust you to do better.)
Here, all we did was take our @filepath{template.txt.p} (which turned an X-expression into a string) and wrap it in the bare minimum LaTeX boilerplate. (Confidential to LaTeX fans: please don't write to complain about my rudimentary LaTeX. It's a tutorial. I trust you to do better.)
Restart the project server to reify the changes to @racket[poly-targets]. When you restart, you'll see a link for @filepath{cv.ltx.pm}. Click it and you'll get this:
@ -324,7 +329,7 @@ That's it. LaTeX achieved.
Still not satisfied? Still want to see one more cute Pollen trick?
OK, you win. Let's not stop at LaTeX — let's go all the way to PDF using the LaTeX PDF converter, known as @exec{pdflatex}. (This is a command-line program that must be installed on your machine for this trick to work.)
OK, you win. Let's not stop at LaTeX — let's go all the way to PDF using the LaTeX PDF converter, known as @exec{pdflatex}. (This is a command-line program that must be installed on your machine for this trick to work. I made this example on OS X 10.9.5.)
How do we do this? We'll follow the pattern we've already established, but with one wrinkle. To make a PDF, we need to generate LaTeX output first. So we actually don't need to add new branches to our tag functions — we'll just let PDF piggyback on our LaTeX branches. The big difference will be in the template, where instead of returning a LaTeX source file, we'll send it through @exec{pdflatex} and get the binary PDF file that results.
@ -332,7 +337,7 @@ First, we update @filepath{pollen.rkt}:
@fileblock["pollen.rkt" @codeblock|{
#lang racket/base
(require racket/date pollen/setup)
(require racket/date txexpr pollen/setup)
(provide (all-defined-out))
(module setup racket/base
@ -342,17 +347,17 @@ First, we update @filepath{pollen.rkt}:
(define (get-date)
(date->string (current-date)))
(define (heading . xs)
(define (heading . elements)
(case (current-poly-target)
[(ltx pdf) (apply string-append `("{\\huge " ,@xs "}"))]
[(txt) (map string-upcase xs)]
[else `(h2 ,@xs)]))
[(ltx pdf) (apply string-append `("{\\huge " ,@elements "}"))]
[(txt) (map string-upcase elements)]
[else (txexpr 'h2 empty elements)]))
(define (emph . xs)
(define (emph . elements)
(case (current-poly-target)
[(ltx pdf) (apply string-append `("{\\bf " ,@xs "}"))]
[(txt) `("**" ,@xs "**")]
[else `(strong ,@xs)]))
[(ltx pdf) (apply string-append `("{\\bf " ,@elements "}"))]
[(txt) `("**" ,@elements "**")]
[else (txexpr 'strong empty elements)]))
}|]
You can see that we've simply added the @racket[pdf] extension in three places: in the list of @racket[poly-targets], and to the @racket[ltx] branches of our tag functions. (In a @racket[case] statement, putting multiple values in a branch means ``match any of these values.'') Easy.
@ -373,7 +378,8 @@ The template, not as easy:
(make-directory working-directory))
◊(define temp-ltx-path (build-path working-directory "temp.ltx"))
◊(display-to-file latex-source temp-ltx-path #:exists 'replace)
◊(define command (format "pdflatex '~a'" temp-ltx-path))
◊(define command (format "pdflatex -output-directory '~a' '~a'"
working-directory temp-ltx-path))
◊(if (system command)
(file->bytes (build-path working-directory "temp.pdf"))
(error "pdflatex: rendering error"))
@ -381,7 +387,7 @@ The template, not as easy:
I know that only the serious nerds are still with me, but let's review what's happening here.
First, we use @filepath{template.pdf.p} rather than @filepath{template.pdf} for our template name. This is the @seclink["Null___p_extension_"] in use. Operating systems assume that files with a @racket[pdf] extension contain binary data, not text. The @racket[p] extension just shields the file from this assumption. It will simply be converted to @filepath{template.pdf} on render.
First, we use @filepath{template.pdf.p} rather than @filepath{template.pdf} for our template name. This is the @seclink["Null___p_extension_"] in use. Operating systems assume that files with a @filepath{.pdf} extension contain binary data, not text. The @filepath{.p} extension just shields the file from this assumption. It will simply be converted to @filepath{template.pdf} on render.
A quick narrative of the rest:
@ -389,7 +395,7 @@ A quick narrative of the rest:
◊(local-require racket/file racket/system)
}|
We need @racket[racket/file] for @racket[display-to-file] and @racket[file->bytes]; we need @racket[racket/system] for @racket[system] (to use the command line).
We need @racketmodname[racket/file] for @racket[display-to-file] and @racket[file->bytes]; we need @racketmodname[racket/system] for @racket[system] (to use the command line).
@codeblock|{
◊(define latex-source ◊string-append{
@ -399,7 +405,7 @@ We need @racket[racket/file] for @racket[display-to-file] and @racket[file->byte
\end{document}})
}|
This is the same as our @filepath{template.ltx} from before, but stored in a variable. The @racket[string-append] is needed here because the curly braces create a list of strings, and we want a single string.
This is the same as our @filepath{template.ltx.p} from before, but stored in a variable. The @racket[string-append] is needed here because the curly braces create a list of strings, and we want a single string.
@codeblock|{
◊(define working-directory
@ -414,7 +420,8 @@ Create a temporary working directory (because @exec{pdflatex} creates a bunch of
@codeblock|{
◊(define command (format "pdflatex '~a'" temp-ltx-path))
◊(define command (format "pdflatex -output-directory '~a' '~a'"
working-directory temp-ltx-path))
◊(if (system command)
(file->bytes (build-path working-directory "temp.pdf"))
(error "pdflatex: rendering error"))
@ -426,12 +433,7 @@ Restart the project server and click on @filepath{cv.pdf.pm}, and you'll see the
@image/rp["poly-ps-pdf.png" #:scale 0.45]
``Why didn't you just write to @filepath{cv.pdf}?'' Because when Pollen is running this render, it expects to end up with the data that it will write to @filepath{cv.pdf}. In previous examples, the templates provided text-based data for Pollen to write into a destination file. In this case, we're providing binary data (which Pollen will handle correctly.) If the template wrote to @filepath{cv.pdf} directly and returned @racket[void], it would be treated as an error.
In fact, because Pollen handles binary files equally well, you could use it to make, say, an audio rendering of a source file. But that will be left as an exercise to you, dear reader.
As usual, you can change the content in @filepath{cv.poly.pm}, or the tag functions in @filepath{pollen.rkt}, and refresh the PDF in the project server to see the result.
@section[#:tag "raco-pollen-render-poly"]{Using @exec{raco pollen render} with @tt{poly} sources}

@ -1,4 +1,4 @@
#lang scribble/manual
#lang scribble/manual
@(require scribble/eval (for-label pollen/unstable/pygments pollen/decode plot pollen/setup pollen/tag racket/base pollen/template txexpr racket/list racket/string))
@(require "mb-tools.rkt")
@ -21,11 +21,11 @@ Pygments is a Python library (though you don't need to know any Python to use it
@subsection[#:tag "pygments-with-pollen"]{Using Pygments with Pollen}
I used @link["http://pygments.org/"]{Pygments} for syntax highlighting in @link["http://unitscale.com/mb/technique/dual-typed-untyped-library.html"]{this recent article made with Pollen}. Links to the source are available at the bottom of the article.
I used @link["http://pygments.org/"]{Pygments} for syntax highlighting in @link["http://unitscale.com/mb/technique/dual-typed-untyped-library.html"]{this article made with Pollen}. Links to the source are available at the bottom of the article.
@itemlist[#:style 'ordered
@item{Make sure you have @code{pygments} already installed. @link["http://pygments.org/download/"]{Instructions here.} Pretty easy — for instance, on my OS X machine, I was able to get it done by doing @code{easy_install pygments} at the command line.}
@item{Make sure you have @code{pygments} already installed. @link["http://pygments.org/download/"]{Instructions here.} Pretty easy — for instance, on my OS X machine, it simply require @code{easy_install pygments} at the command line.}
@item{The @racketmodname[pollen/unstable/pygments] helper module provides a function called @racket[highlight]. To make @racket[highlight] available in your source file, you can either add the line @code{◊(require pollen/unstable/pygments)} to the source file itself, or put it in @racket["pollen.rkt"] and @racket[provide] it from there.}
@ -43,7 +43,7 @@ for x in range(3):
When you run this file, you should see something like this, with the parsed syntax marked up into an X-expression:
@repl-output{
'(div ((class "highlight")) (table ((class "sourcetable")) (tbody () (tr () (td ((class "linenos")) (div ((class "linenodiv")) (pre () "1\n2"))) (td ((class "code")) (div ((class "source")) (pre () (span ((class "k")) "for") " " (span ((class "n")) "x") " " (span ((class "ow")) "in") " " (span ((class "nb")) "range") (span ((class "p")) "(") (span ((class "mi")) "3") (span ((class "p")) "):") "\n " (span ((class "k")) "print") " " (span ((class "n")) "x") "\n")) "\n")))) "\n")
'(div ((class "highlight")) (table ((class "sourcetable")) (tbody (tr (td ((class "linenos")) (div ((class "linenodiv")) (pre "1\n2"))) (td ((class "code")) (div ((class "source")) (pre (span ((class "k")) "for") " " (span ((class "n")) "x") " " (span ((class "ow")) "in") " " (span ((class "nb")) "range") (span ((class "p")) "(") (span ((class "mi")) "3") (span ((class "p")) "):") "\n " (span ((class "k")) "print") " " (span ((class "n")) "x") "\n")) "\n")))) "\n")
}
}
@ -52,7 +52,9 @@ When you run this file, you should see something like this, with the parsed synt
]
I concede that the last step isnt super convenient. But I havent yet figured out how it to make it easier. Larding the Pollen distribution with a bunch of Pygments themes doesnt make sense. Moreover, even if convenient, they wouldnt be editable / programmable, which is sort of the point of the whole exercise.
I concede that the last step isnt convenient. But I havent yet figured out how it to make it easier. Larding the Pollen distribution with a bunch of Pygments themes doesnt make sense. Moreover, even if convenient, they wouldnt be editable / programmable, which is sort of the point of the whole exercise.
Anyhow, that's why it's in the @code{unstable} category — it works, but I think it could be done better.
@subsection{Using Highlight.js with Pollen}
@ -63,9 +65,9 @@ Because @link["https://highlightjs.org/"]{Highlight.js} is browser-based, it doe
@item{Download the @link["https://highlightjs.org/usage/"]{Highlight.js} library.}
@item{Add these lines to the @code{<head>} section of your @racket["template.html"] (or other template):
@item{Add these lines to the @code{<head>} section of your @filepath{template.html} (or other template):
@repl-output{
@terminal{
<link rel="stylesheet" href="/path/to/styles/default.css">
<script src="/path/to/highlight.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
@ -105,9 +107,9 @@ As above, I concede that it would be more convenient to have Pollen automaticall
@item{Download the @link["http://docs.mathjax.org/en/latest/start.html"]{MathJax} library if you want to run the library locally, without a network connection. You can also use the MathJax CDN and just link to the library across the network (I'll use this option in the example that follows).}
@item{Add these lines to the @code{<head>} section of your @racket["template.html"] (or other template). First, the MathJax library itself:
@item{Add these lines to the @code{<head>} section of your @filepath{template.html} (or other template). First, the MathJax library itself:
@repl-output{
@terminal{
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
@ -115,7 +117,7 @@ As above, I concede that it would be more convenient to have Pollen automaticall
Then, add any configuration options. For instance, this will activate the dollar sign as an inline-equation delimiter:
@repl-output{
@terminal{
<script type="text/x-mathjax-config">
MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$']]}});
</script>
@ -135,7 +137,7 @@ Then, add any configuration options. For instance, this will activate the dollar
]
Putting it together, here's a minimal working example in two files (obviously in a larger project, you'd move those tag functions to a @racket["pollen.rkt"] file):
Putting it together, here's a minimal working example in two files (obviously in a larger project, you'd move those tag functions to a @filepath{pollen.rkt} file):
@fileblock["equation.html.pm"
@codeblock{
@ -150,7 +152,7 @@ Putting it together, here's a minimal working example in two files (obviously in
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
@ -168,3 +170,4 @@ MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$']]}});
</html>
}]
By the way, there's no @code{pollen/math} module because this task doesn't seem complicated enough to merit it. What I just described is all you need.

@ -1,6 +1,6 @@
#lang scribble/manual
@(require (for-label racket/base pollen/setup pollen/template pollen/pagetree sugar))
@(require (for-label pollen/render racket/base pollen/core txexpr pollen/setup pollen/template pollen/pagetree sugar))
@(require "mb-tools.rkt")
@ -28,33 +28,39 @@ If you want the shortest possible introduction to Pollen, try the @secref["quick
I'll assume you've completed the @seclink["first-tutorial"]{first tutorial} and you understand how to create source files in DrRacket and view them in the project server. I won't be spelling out those tasks as I did before.
@section{The case against Markdown}
@section[#:tag "the-case-against-markdown"]{Optional reading: the case against Markdown}
I recognize that people like Markdown. I want people to like Pollen too, so that's why Pollen supports Markdown.
But just to be clear about my own views.
But just to be clear about my own views:
I'm mystified by the popularity of Markdown among writers. I can agree that it's a clever and readable way of notating basic HTML. And sure, that makes it great for things like web comments, where speed and simplicity are primary virtues.
In longer-form writing, however, its shortcomings become evident. Like programming languages, the best writing tools maximize expressive possibilities, and minimize constraints. But Markdown is hugely constrained. First and worst, Markdown isn't semantic. It only knows about formatting, and in that regard, isn't that much of an improvement on tools like Microsoft Word. Second, even as a formatting-notation tool, it's limited to a small subset of the already-small set of formatting tags permitted in HTML. Third, it can't be extended by an author.
An animating principle of Pollen, as explained in the @secref["Backstory"], is that after 20 years, we ought to move beyond thinking of HTML as a source format. Since Markdown is just well-disguised HTML, a vote for Markdown is really a vote to continue the status quo (albeit with fewer angle brackets). For me, that's not good enough. I'm ready for the tools to expand to fit my ideas; I don't want to keep cutting down my ideas to fit the tools.
An animating principle of Pollen, as explained in the @secref["Backstory"], is that after 20 years, we ought to move beyond thinking of HTML as a source format. Since Markdown is just well-disguised HTML, a vote for Markdown is really a vote to continue the status quo (albeit with fewer angle brackets). For me, that's not good enough. I'm ready for the tools to expand to fit my ideas. I refuse to keep cutting down my ideas to fit the tools.
All that said, if you genuinely prefer Markdown, I'm not looking to pry it from your fingers. Pollen has great Markdown support (due entirely to Greg Hendershott's excellent @link["https://github.com/greghendershott/markdown/"]{Markdown parser} for Racket). It makes Markdown more useful.
But let's make a deal, Markdown fans. Having met you more than halfway, will you at least consider that @seclink["Pollen_markup_vs__XML"]{Pollen markup} might be a better option for you than Markdown? Because it can notate anything that's in your brain, not just a subset of HTML? And if @secref["the-book-is-a-program"], the source for that book should look more like your brain, and less like HTML?
But let's make a deal, Markdown fans. Having met you more than halfway, will you at least consider that @seclink["pollen-vs-xml"]{Pollen markup} might be a better option for you than Markdown? Because it can notate anything that's in your brain, not just a subset of HTML? And if @secref["the-book-is-a-program"], the source for that book should look more like your brain, and less like HTML (or XML or LaTeX or ...)?
That's all I ask.
@section{Markdown in Pollen: two options}
There are two ways to use Markdown within Pollen: you can either send Markdown files through the preprocessor, or use Markdown authoring mode.
There are two ways to use Markdown within Pollen:
@itemlist[#:style 'ordered
@item{Send Markdown files through the preprocessor.}
@item{Use Markdown authoring mode.}]
The preprocessor approach is better if you want to end up with a set of Markdown files that can be passed along to a HTML converter (or other Markdown-to-______ converter) downstream.
The authoring-mode approach is better if you want to end up with something other than Markdown, e.g., finished HTML files.
But we'll look at both.
@subsection{Using Markdown with the preprocessor}
Because Markdown is a text-based format, you can use the Pollen preprocessor to add programmatic features to existing Markdown files. (See @secref["Working_with_the_preprocessor"] in the @seclink["first-tutorial"]{first tutorial} if you need a refresher.)
@ -127,70 +133,86 @@ Though the preprocessor is useful, it limits you to inserting chunks of text at
Pollen's @defterm{authoring mode}, by contrast, parses the whole source file into a special data structure called an @defterm{X-expression}. You can then process the whole X-expression any way you like, and output to any format you like — or multiple formats — using a @defterm{template}.
Compared to the preprocessor, authoring mode offers more abstraction and flexibility. Of course, it's also a bit more effort to set up.
Compared to the preprocessor, authoring mode offers more abstraction and flexibility. Of course, it also requires a bit more effort to set up.
Pollen offers two variants of authoring mode: one that uses Markdown syntax (which we'll cover later in this tutorial) and the other that uses a free-form markup syntax (which we'll cover in the @secref["third-tutorial" #:doc '(lib "pollen/scribblings/pollen.scrbl")]). In both cases, the basic process is the same: 1) compile the source into an X-expression, and then 2) convert that X-expression to the target file format using a template.
Pollen offers two authoring modes: one that uses Markdown syntax (which we'll cover later in this tutorial) and the other that uses a free-form markup syntax (which we'll cover in the @seclink["third-tutorial"]{third tutorial}). In both cases, the basic process is the same: 1) compile the source into an X-expression, and then 2) convert that X-expression to the target file format using a template.
@subsection{X-expressions}
@(noskip-note)
I avoid nerdy jargon whenever possible. But in this case, the thing is called an @defterm{X-expression} throughout the Racket documentation, for good reasons. So I use the term too. Better to acclimate you now.
I avoid nerdy jargon when I can. But in this case, the thing is called an @defterm{X-expression} throughout the Racket documentation, for good reasons. So I use the term too. Better to acclimate you now.
An X-expression is a way of representing markup-based data in code. X-expressions are indigenous to Lisp-based languages like Pollen and Racket. They don't exist in Python or JavaScript or Ruby.
Let's start with the part you're familiar with. By ``markup-based data,'' I mean things like HTML and XML and SVG. The idea is that you have text-based data surrounded by @defterm{tags}. Each tag can also have its own @defterm{attributes} that are made of keys and values. Tags can contain other tags, thus creating a tree-like structure. Right? You know what I mean:
@terminal{<body><h1>Hello world</h1><p class="first">Nice to <i>see</i> you.</p></body>}
@terminal{<body>
  <h1>Hello world</h1>
  <p class="first">Nice to <i>see</i> you.</p>
</body>}
An X-expression is just a simplified, generalized method of notation for these data structures — much like Markdown is a simplified method of notation for HTML. To see the relationship, we'll convert one into the other.
First, we change the angle brackets to parentheses, and only use them on the outside of tags:
@terminal{(body (h1 Hello world /h1) (p class="first" Nice to (i see /i) you. /p) /body)}
@terminal{(body
  (h1 Hello world /h1)
  (p class="first" Nice to (i see /i) you. /p)
/body)}
Then we get rid of the closing tags, which are superfluous, since each closing parenthesis adequately marks the end of the tag:
Then we get rid of the closing tags, which are superfluous, since each closing parenthesis suffices to mark the end of a tag:
@terminal{(body (h1 Hello world) (p class="first" Nice to (i see) you.))}
@terminal{(body
  (h1 Hello world)
  (p class="first" Nice to (i see) you.))}
However, this creates ambiguity between the name of the tag and the content. So we'll put the content within double quotes:
@terminal{(body (h1 "Hello world") (p class="first" "Nice to" (i "see") "you."))}
@terminal{(body
  (h1 "Hello world")
  (p class="first" "Nice to" (i "see") "you."))}
As for the @code{class} attribute, we need to distinguish it from both the markup tags and the content, so we'll move it into another pair of parentheses:
As for the @code{class} attribute, we need to distinguish it from both the markup tags and the content, so we'll move it between double parentheses:
@terminal{(body
  (h1 "Hello world")
  (p ((class "first")) "Nice to" (i "see") "you."))}
Whitespace isn't significant, so the X-expression can also be written without linebreaks:
@terminal{(body (h1 "Hello world") (p ((class "first")) "Nice to" (i "see") "you."))}
Skipping past a few boring details, that's basically all there is to it.
Skipping a few boring details, that's basically all there is to it.
So why is it called an X-expression? Lisp languages are built out of units called S-expressions, which look like this:
@terminal{(and (txexpr? x) (memq (get-tag x) (setup:block-tags)) #t))}
@terminal{(and (txexpr? x) (memq (get-tag x) (setup:block-tags)))}
S-expressions use prefix notation, where each pair of parentheses contains a list. The first element in the list names a function, and the other elements are the arguments to that function. (This is a review of @secref["Racket_basics__if_you_re_not_familiar_"].) X-expressions are just a minor adaptation of S-expression notation to represent markup, hence the name (the @defterm{X} is short for @defterm{XML-like}).
S-expressions use prefix notation, where each pair of parentheses contains a list. The first element in the list names a function, and the other elements are the arguments to that function. (This is a review of @secref["Racket_basics__if_you_re_not_familiar_"].) X-expressions just adapt S-expression notation to represent markup, hence the name (the @defterm{X} is short for @defterm{XML-like}).
For handling markup-based data, X-expressions have some useful advantages compared to other methods:
For handling markup-based data, X-expressions have some clear advantages over other methods:
@itemlist[
@item{@bold{Readability.} X-expressions retain all the semantics of markup-based data while dispensing with the infamous verbosity.}
@item{@bold{Readability.} X-expressions retain the semantics of markup-based data while dispensing with the infamous verbosity.}
@item{@bold{A hybrid between a tree and a string.} Most programming languages represent markup-based data either as a string or as an XML tree. Neither is a good choice. The string captures none of the internal structure of the data. An XML tree captures the structure, but conceals the sequential nature of the data elements. The X-expression gets both.}
@item{@bold{A hybrid between a tree and a string.} Most programming languages represent markup-based data either as a string or as an XML tree. Neither is a good choice. The string captures none of the internal structure of the data. An XML tree captures the structure, but conceals the sequential nature of the data elements. The X-expression shows both.}
@item{@bold{An ideal match for an expression-based programming language.} Aside from some notational details, X-expressions are just a subset of S-expressions generally, which are the building block of Racket. Processing X-expressions in Racket maximizes flexibility and minimizes @link["http://programmers.stackexchange.com/questions/34775/correct-definition-of-the-term-yak-shaving"]{yak-shaving}.}
]
@margin-note{Given the close kinship between XML-ish data structures and Lisp-ish programming languages, I have no explanation why, during the Internet era, they have not been paired more often. They're like peanut butter and jelly.}
In Pollen's authoring modes, your source file is parsed into an X-expression, which can then be processed further before being injected into a template & converted to output. As a first example, we'll look at Markdown authoring mode.
In Pollen's authoring modes, your source file is compiled into an X-expression, which is then injected into a template & converted to output. As a first example, we'll look at Markdown authoring mode.
@subsection{Markdown authoring mode}
Let's start putting together our article. For simplicity, I'm going to use unrealistically short sample texts. But you can use whatever Markdown content you want.
Let's start putting together our multiple-page article. For simplicity, I'm going to use unrealistically short sample texts. But you can use whatever Markdown content you want.
We want to use Markdown authoring mode to make a file that will ultimately be HTML. So consistent with Pollen file-naming conventions (see @secref["Saving___naming_your_source_file"]), we'll start with our desired output filename, @filepath{article.html}, and then append the Markdown authoring suffix, @filepath{.pmd}. So in DrRacket, start a new file called @filepath{article.html.pmd} and put some Markdown in it:
We want to use Markdown authoring mode to make a file that will ultimately be HTML. So consistent with Pollen file-naming conventions (see @secref["Saving___naming_your_source_file"]), we'll start with our desired output filename, @filepath{article.html}, and then append the Markdown authoring suffix, which is @filepath{.pmd}. So in DrRacket, start a new file called @filepath{article.html.pmd} and put some Markdown in it:
@fileblock["article.html.pmd"
@codeblock{
@ -204,21 +226,22 @@ I am **so** happy to be writing this.
Before you preview this file in the project server, click the @onscreen{Run} button in DrRacket just to see what the file produces. You'll see something like this:
@repl-output{'(root (h1 ((id "my-article")) "Deep Thought") (p () "I am "
(strong () "so") " happy to be writing this."))}
@repl-output{'(root (h1 ((id "deep-thought")) "Deep Thought")
(p "I am " (strong "so") " happy to be writing this."))}
You should now be able to recognize this as an X-expression. In authoring mode, Pollen parses your Markdown into the corresponding HTML entities, but then provides the data as an X-expression rather than finished HTML.
You should now be able to recognize this as an X-expression. In authoring mode, Pollen compiles your Markdown into the corresponding HTML entities, but then provides the data as an X-expression rather than finished HTML.
From what you learned in the last section, it should be evident that this X-expression will convert to HTML that looks like this:
@margin-note{The empty parentheses @code{()} after @code{p} and @code{strong} signal that the tag's attributes are empty. When you write an X-expression without attributes, these parentheses are optional — @code{(tag () "text")} and @code{(tag "text")} are equivalent — but Pollen will always print X-expressions this way.}
@terminal{<root><h1 id="deep-thought">Deep Thought</h1>
<p>I am <strong>so</strong> happy to be writing this.</p></root>}
From what you learned in the last section, it should be evident that this X-expression corresponds to HTML that looks like this:
``But what's this @code{root} tag? That's not HTML.'' An X-expression that holds other X-expressions must have a root tag. So in the spirit of obviousness, every X-expression produced by Pollen in authoring mode will start with @code{root}. If you don't need it, you can discard it (we'll cover this below, in @secref["Templates"]). Though as you'll learn in the @seclink["third-tutorial"]{third tutorial}, @code{root} also creates a useful hook for further processing — it's not a superfluous accessory.
@repl-output{<root><h1 id="my-article">Deep Thought</h1><p>I am @(linebreak)<strong>so</strong> happy to be writing this.</p></root>}
``But what's this @code{root} tag? That's not HTML.'' An X-expression must have a root tag, so in the spirit of obviousness, every X-expression produced by a source file in authoring mode will start with @code{root}. If you don't need it, you can discard it. But it also creates a useful hook for further processing, as we'll see later.
@subsection{Review: authoring mode vs. preprocessor mode}
By the way, as review, let's remind ourselves how this is different from preprocessor mode. Let's take the same Markdown content, but this time put it into a preprocessor source file called @filepath{article.md.pp}.
Before we proceed, let's quickly review how authoring mode is different from preprocessor mode. To do that, let's take the same Markdown content, but this time put it into a preprocessor source file called @filepath{article.md.pp}.
@fileblock["article.md.pp"
@codeblock{
@ -241,11 +264,11 @@ I am **so** happy to be writing this.
This result makes sense, right? To recap: when you use Markdown source in preprocessor mode, Pollen gives you Markdown. When you use Markdown source in authoring mode, Pollen gives you an X-expression.
So how do you convert an X-expression into your target file format? Read on.
So how do you convert an X-expression into HTML? Read on.
@section[#:tag-prefix "tutorial-2"]{Templates}
In Pollen, a @defterm{template} is where you take data from an X-expression and convert it to your target format. If you've used other web-publishing systems, templates are probably a familiar idea. Templates in Pollen are similar to the ones you've seen before in some ways, but different in other ways.
In Pollen, a @defterm{template} lets you convert an X-expression to your target output format. If you've used other web-publishing systems, templates are probably a familiar idea. Templates in Pollen are both similar and different.
First, the two major similarities:
@ -253,24 +276,24 @@ First, the two major similarities:
@item{At its simplest, a template holds @bold{boilerplate material} that you want to reuse across multiple output files. For instance, in a set of HTML pages, you might have layout and navigation elements that stay the same, while the content changes. In that case, you could put the layout and navigation in the template, and keep the content in your Pollen source files. When you want to add a new page, you can make a new source file and just use it with the existing template. Moreover, if you want to change the layout and navigation globally, you can just change the template, rather than changing the source files.}
@item{Pollen templates, like others, can also have @bold{conditional elements} — meaning, you can embed simple code in your templates that allows them to change based on the content in the page. For instance, a template could show or hide ``previous page'' and ``next page'' links depending on whether there's actually a previous or next page.}
@item{Pollen templates, like others, can also have @bold{conditional elements} — meaning, you can embed simple code in your templates that allows them to change based on the content in the page. For instance, a template could show or hide ``previous page'' and ``next page'' links depending on whether there's actually a previous or next page. (Jump ahead to the @seclink["third-tutorial"]{third tutorial} if this excites you.)}
]
And two major differences:
@margin-note{Under the hood, a template is not a self-contained source file, but rather a fragment of source code that gets evaluated in the context of other code (namely, your textual content). Because of this, however, you can't use @racket[require] in a template to import external libraries, because @racket[require] can only be used in a self-contained source file. But there's a simple workaround — instead, use @racket[local-require], which does the same thing.}
@itemlist[
@item{There's @bold{no special template language} you need to learn, with magic syntax and whatnot. Instead, you can use all the same Pollen commands in a template that you can in authoring mode or preprocessor mode. (With one major exception — see the margin note.)}
@item{There's @bold{no special template language} you need to learn, with magic syntax and whatnot. Instead, you can use all the same Pollen commands in a template that you can in authoring mode or preprocessor mode.}
@item{Within a template, you have to @bold{explicitly convert} the X-expression to the target format. This is a feature, not a bug. By avoiding assumptions about the target format, Pollen templates can be used to generate any kind of file (even binary formats like PDF). But the cost of this flexibility is that you need to tell Pollen what you want.}
]
@margin-note{``So a template is also a Pollen source file?'' Not quite. More accurately, it's a fragment of Pollen source that is completed by adding the X-expression that comes out of one of your source files. Because of this, there are a few extra limitations on the code you can put in a template, though with easy workarounds (for instance, you can't use @racket[require] in a template, but you can use @racket[local-require], which accomplishes the same thing).}
To see how this works, let's return to the source file we started in the last section:
@fileblock["article.html.pmd"
@ -293,20 +316,18 @@ I am @bold{so} happy to be writing this.
Here, you're seeing the X-expression from your source combined with an HTML template, which adds the necessary boilerplate for the finished HTML:
@repl-output{
<html><head><meta charset="UTF-8" /></head><body>
<root><h1 id="my-article">Deep Thought</h1><p>I am
<strong>so</strong> happy to be writing this.</p></root>
@terminal{
<html><head><meta charset="UTF-8" /></head><body><root><h1 id="my-article">Deep Thought</h1><p>I am <strong>so</strong> happy to be writing this.</p></root>
</body></html>
}
But wait — where did the template come from? When you view an authoring-mode source file in the project server without specifying a template, but you're using an @filepath{html} extension, Pollen helps you out and uses its @defterm{fallback template} for HTML. The fallback template is just a minimal template that's used as a last resort. Under ordinary circumstances, seeing the fallback template usually signals a problem (e.g., Pollen couldn't find the template you asked for).
But wait — where did the template come from? If you try to render a file to HTML without first setting up a template, Pollen helps you out and uses its @defterm{fallback template} for HTML. The fallback template is just a minimal template that's used as a last resort. Under ordinary circumstances, seeing the fallback template probably signals a problem (e.g., Pollen couldn't find the template you asked for). For now, we can rely on it to get a preview of our file.
But we can learn a few things from the fallback template about how to make an HTML template.
Still, we can learn a few things about how to make an HTML template by studying the fallback template.
@subsection{The @tt{doc} export and the @tt{->html} function}
To understand the necessary ingredients of a template, let's look at a simple one — the fallback template that Pollen uses for HTML files, called @filepath{fallback.html}.
To understand the necessary parts of a template, let's look at a simple one — the fallback template that Pollen uses for HTML files, called @filepath{fallback.html}.
@fileblock["fallback.html"
@codeblock[#:keep-lang-line? #f]{
@ -314,9 +335,9 @@ To understand the necessary ingredients of a template, let's look at a simple on
◊(->html (html (head (meta #:charset "UTF-8")) (body doc)))
}]
It has three key ingredients.
The fallback template has three key ingredients:
First, there's an X-expression that represents a basic HTML page:
The first ingredient is an X-expression that represents a basic HTML page:
@codeblock[#:keep-lang-line? #f]{
#lang pollen
@ -327,94 +348,108 @@ That X-expression is equivalent to this HTML string:
@terminal{<html><head><meta charset="UTF-8"></head><body></body></html>}
But within a template, we need to tell Pollen how we want to convert the X-expression to the target format. explicitly convert from X-expression to HTML. So we wrap this X-expression with our second key ingredient, the Pollen command @racket[->html]:
But how do we get from X-expression to HTML? Here's how: within the template, we'll explicitly convert this X-expression to HTML. We do this by sending the X-expression through our second key ingredient, the function @racket[->html]:
@codeblock[#:keep-lang-line? #f]{
#lang pollen
◊(->html (html (head (meta #:charset "UTF-8")) (body)))
}
Third, we need to include the content from our source file. By convention, every Pollen source file makes its output available through an exported variable named @code{doc}. A source file in preprocessor mode puts its text result in @code{doc}. And a source file in authoring mode puts its X-expression result in @code{doc}. So we put the variable @code{doc} inside the @code{body} tag.
@racket[->html] is in the @racketmodname[pollen/template] module, but this module is automatically imported into every template, so there's nothing extra we need to do.
@margin-note{You can change the name to something other than @code{doc} by changing @racket[default-main-export].}
Finally, we need to include the X-expression from our source file. By convention, every Pollen source file makes its output available through an exported variable called @code{doc}. A source file in preprocessor mode puts its text result in @code{doc}. And a source file in authoring mode puts its X-expression result in @code{doc}. So we put the third key ingredient, the variable @code{doc}, inside our @code{body} tag.
@codeblock[#:keep-lang-line? #f]{
#lang pollen
◊(->html (html (head (meta #:charset "UTF-8")) (body doc)))
}
Under the hood, a template is just a partial program that relies on a set of variables defined by another source file. (In Racket, this set of variables is called a @defterm{lexical context}). So if you ran this template on its own, nothing would happen, because @code{doc} isn't defined. But when you run it in the context of another source file, it picks up the @code{doc} that's defined by that file.
Caution — despite the name, a Pollen template is not necessarily a file of the type suggested by its extension. For instance, @filepath{fallback.html} is a file that ultimately produces HTML, but as the example above shows, it's not necessarily written in HTML.
@margin-note{You can change the name of @code{doc} by overriding @racket[default-main-export].}
To summarize: this template contains a skeletal HTML page (in X-expression format). We drop @code{doc} into the template to indicate where the X-expression of our source file should be inserted. Finally, we convert the whole X-expression to HTML with @racket[->html].
It could be, however. Here's an equivalent way of writing @filepath{fallback.html} that inserts @code{doc} into actual HTML, rather than making the whole thing an X-expression.
``So I have to convert my HTML template to an X-expression?'' No. That's optional. You can also put hard-coded HTML in your template. Here's an equivalent way of writing @filepath{fallback.html.p}, with explicit HTML:
@fileblock["fallback.html" @codeblock[#:keep-lang-line? #f]{
@fileblock["fallback.html.p" @codeblock[#:keep-lang-line? #f]{
#lang pollen
<html><head><meta charset="UTF-8"></head>
<body>◊(->html doc)</body></html>
<html>
<head><meta charset="UTF-8"></head>
<body>◊(->html doc)</body>
</html>
}]
Notice that we still need to use the @racket[->html] function, but this time, instead of surrounding a larger X-expression, it just goes around @code{doc}.
Notice that we still need to use the @racket[->html] function, but this time, instead of surrounding a larger X-expression, it just goes around @code{doc}. The rest of the template code gets passed through as plain text.
Truly, there is no difference between these two methods. In the first method, you're describing the whole template using an X-expression and converting the whole thing with @racket[->html]. In the second method, you're ``hard-coding'' the boilerplate HTML, so the only part that needs to be converted with @racket[->html] is @code{doc}.
What matters most is ending up with HTML. Otherwise, there is no difference between these two approaches. In the first, you're describing the whole template using an X-expression and converting the whole thing with @racket[->html]. In the second, you're hard-coding the boilerplate HTML, so the only part that needs to be converted with @racket[->html] is @code{doc}.
Use whichever method works best for you. I often prefer the second method, because I like to build HTML layouts by hand using placeholder content to make sure all the fiddly bits work. Then it's easy to replace the placeholder content with @racket[(->html doc)], and it becomes a template.
On the other hand, when you use X-expressions to generate HTML, the result is always perfect. You'll never have to comb through a template looking for a missing closing @code{</div>} tag.
@subsection{Making a custom template}
We'll use these three ingredients to make our own template for @filepath{article.html.pmd}.
Having learned about the three key template ingredients, we'll now make a custom template for @filepath{article.html.pmd}.
In general, template files can have any name you want. But by default, Pollen will first look for a file in your project directory called @filepath{template.ext}, where @code{ext} matches the output-file extension of the source file. So if your source file is @filepath{database.xml.pmd}, Pollen will look for @filepath{template.xml}. And for @filepath{article.html.pmd}, Pollen will look for @filepath{template.html}.
In general, template files can have any name you want. But by default, Pollen will first look for a file in your project directory called @filepath{template.ext}, where @filepath{ext} matches the output-file extension of the source file. So if your source file is @filepath{database.xml.pmd}, Pollen will look for @filepath{template.xml}. And for @filepath{article.html.pmd}, Pollen will look for @filepath{template.html}.
Therefore, to set up a custom template, all we need to do is create a file called @filepath{template.html} in our project directory, and make sure it has the three key ingredients we saw in the fallback template. Pollen will automatically apply it to @filepath{article.html.pmd} when we view it in the project server.
@margin-note{If you want to use a template with a different name, or apply a special template to a particular source file, you can specify a template from within any source file by inserting the line @code{◊(define-meta template "my-template-name.html")}. For more, see @racket[get-template-for].}
But don't take my word for it. In your project directory, create a new file called @filepath{template.html}:
Furthermore, it's also fine to use a Pollen source file that will result in @filepath{template.html}. Following the fallback-template example above, we'll use the null extension and name our template @filepath{template.html.p}  Pollen will convert this to the @filepath{template.html} it needs.
@fileblock["template.html"
Beyond that, all we need to do make sure our template has the three key ingredients we saw in the fallback template. Pollen will automatically apply it to @filepath{article.html.pmd} when we view it in the project server.
In your project directory, create a new file called @filepath{template.html.p}:
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head><meta charset="UTF-8">
<title>Custom template</title></head>
<head>
<meta charset="UTF-8">
<title>Custom template</title>
</head>
<body>◊(->html doc)</body>
</html>
}]
Recall from the last section that this is the same as the fallback template, but written out in HTML, and with a @code{title} element added. In fact, you can now refresh @filepath{article.html} in the project server. Does it look different? No — it won't, because the resulting template is the same. You should notice, however, that the title of the browser window is now @onscreen{Custom template}, because Pollen is relying on your new template file, rather than the fallback template.
The @filepath{p} extension is Pollen's @seclink["Null___p_extension_"]{@defterm{null extension}}, which does what you might guess — nothing. The template would still work if it were called @filepath{template.html}. But using the null extension is a virtuous habit, because it keeps your templates distinct from output files (both for your sake, and your operating system, which may get flummoxed by things like @filepath{template.pdf}, which would be a text-based file with a binary extension.
Otherwise, this is the same as the fallback template we saw in the previous section, but written out in HTML, and with a @code{title} element added. Refresh @filepath{article.html} in the project server. Does it look different? No — it won't, because the template is basically the same. But because of the new @code{title} field, you should notice that the title shown in the browser window is @onscreen{Custom template}. This shows that Pollen is correctly relying on our new template file, rather than the fallback template.
Let's change our custom template by adding a @code{style} block:
Let's keep going, and add a @code{style} block to our custom template:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head><meta charset="UTF-8">
<title>Custom template</title>
<style type="text/css">
body {padding: 3em; font-size: 20px;}
h1 {background: gray; color: white;}
strong {color: red;}
</style></head>
<head>
<meta charset="UTF-8">
<title>Custom template</title>
<style type="text/css">
body {padding: 3em; font-size: 20px;}
h1 {background: gray; color: white;}
strong {color: red;}
</style>
</head>
<body>◊(->html doc)</body>
</html>}]
When you refresh @filepath{article.html} in the project server, you'll see that the heading now has a gray background, and one word in the text is red.
Refresh @filepath{article.html} in the project server again. Now you'll see that the heading has a gray background, and one word in the text is red.
Feel free to add other settings to @filepath{template.html}, or update the text in @filepath{article.html}, and see how the page changes. As you'd expect, the project server keeps an eye on both your source files and your template files, and if one changes, it will refresh the output file automatically.
Feel free to add other settings to @filepath{template.html.p}, or update the text in @filepath{article.html.pmd}, and see how the page changes in the browser. As you'd expect, the project server keeps an eye on both your source files and your template files, and if one changes, it regenerates the output file.
@subsection{Inserting specific source data into templates}
In the last example, we used @code{doc} to insert the entire content of the source file — as an X-expression — into the template.
In the previous example, we used @code{doc} to insert the entire content of the source file — as an X-expression — into the template.
But what if you want to only insert part of your source file into the template? For instance, you'll look like a dork if the title on each page is @onscreen{Custom template}. So let's fix that.
When you're working in a template, Pollen provides a @racket[select] function that lets you extract the content of a specific tag, like so: @code{◊(select tag-name doc)}, which means ``get the content of @racketvarfont{tag-name} out of @code{doc} and put it here.''
Let's suppose that we'd rather use the name of the article — @italic{Deep Thought} — as the page title. We're going to put a @racket[select] command inside the @code{<title>} tag.
Let's suppose that we'd rather use the name of the article — @italic{Deep Thought} — as the page title. To do this, we'll put a @racket[select] command inside the @code{<title>} tag.
Beyond that, we just need to know the tag name that contains the title. If we have a little Markdown expertise, we might already know that this part of our Markdown source:
To make @racket[select] work, we also need to know the tag name that contains the title. If we have a little Markdown expertise, we might already know that this part of our Markdown source:
@codeblock[#:keep-lang-line? #f]{
@ -425,81 +460,87 @@ Deep Thought
is going to produce a tag named @code{h1}.
What if we don't have all the Markdown conversions memorized? No problem. We can still figure out the tag name by running the @filepath{article.html.pmd} source file in DrRacket and looking at the X-expression that results:
What if we don't have all the Markdown conversions memorized? No problem. We can still figure out the tag name by running the @filepath{article.html.pmd} source file in DrRacket and looking at the resulting X-expression:
@repl-output{'(root (h1 ((id "my-article")) "Deep Thought") (p () "I am "
(strong () "so") " happy to be writing this."))}
@repl-output{'(root (h1 ((id "my-article")) "Deep Thought") (p "I am "
(strong "so") " happy to be writing this."))}
Either way, now we know that the text @italic{Deep Thought} lives in the @code{h1} tag. So we update our template accordingly (for brevity, I'm going to omit the @code{style} tag in these examples, but it's fine to leave it in):
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head><meta charset="UTF-8">
<title>◊(select 'h1 doc)</title></head>
<head>
<meta charset="UTF-8">
<title>◊(select 'h1 doc)</title>
</head>
<body>◊(->html doc)</body>
</html>
}]
When you refresh the page in the project server, the page title will now appear as @onscreen{Deep Thought}. Of course, you can also combine static and dynamic elements in your template, like so:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head><meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title></head>
<head>
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
</head>
<body>◊(->html doc)</body>
</html>
}]
The page title will now be @onscreen{Deep Thought, by MB}.
A couple notes on command syntax. We inserted the @racket[select] and @racket[->html] commands using Racket-mode syntax. We could also use Pollen-mode syntax and write the commands this way:
A couple notes on command syntax. We wrote the @racket[select] and @racket[->html] commands in Racket style. We could also write the commands in Pollen style, like so:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head><meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title></head>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
</head>
<body>◊->html[doc]</body>
</html>
}]
This is exactly equivalent to the previous example. Skeptics are welcome to confirm this by checking the result in the project server.
This is exactly equivalent to the previous example. Skeptics are welcome to confirm this by reloading the page in the project server.
Finally, notice that in the @racket[select] command, the tag name @code{h1} is written with a quote mark (@code{'h1}), whereas @code{doc} is not. This is an easy place to get tripped up, but the rule is simple: you don't use a quote mark when you're referring to the name of an existing function or variable (like @racket[select] or @code{doc}). But you do need a quote mark when you're using the text as a literal value.
Finally, notice that in the @racket[select] command, the tag name @code{h1} is written with a quote mark (@code{'h1}), whereas @code{doc} is not. This is an easy place to get tripped up, but the rule is simple: you don't use a quote mark when you're referring to an @defterm{identifier} — that is, the name of an existing function or variable (like @racket[select] or @code{doc}). But you do need a quote mark when you're using the thing as a literal value.
@margin-note{Racket (and hence Pollen) makes a distinction between @secref["symbols" #:doc '(lib "scribblings/guide/guide.scrbl")] (e.g. @racket['h1]) and @secref["strings" #:doc '(lib "scribblings/reference/reference.scrbl")] (e.g. @racket["h1"]). Without getting into the weeds, just note for now that the tag of an X-expression is always a symbol, not a string. But if you write @racketfont*{◊(@racket[select] "h1" doc)}, the command will still work, because Pollen will treat it as @racketfont*{◊(@racket[select] 'h1 doc)}, consistent with a general policy of not being persnickety about input types when the intention is clear.}
@margin-note{Racket (and hence Pollen) makes a distinction between @secref["symbols" #:doc '(lib "scribblings/guide/guide.scrbl")] (e.g. @racket['h1]) and @secref["strings" #:doc '(lib "scribblings/reference/reference.scrbl")] (e.g. @racket["h1"]). Without getting into the weeds, just know for now that the tag of an X-expression is always a symbol, not a string. But if you write @racketfont*{◊(@racket[select] "h1" doc)}, the command will still work, because Pollen will treat it as @racketfont*{◊(@racket[select] 'h1 doc)}, consistent with a general policy of not being persnickety about input types when the intention is clear.}
@subsection{Linking to an external CSS file}
If you're a super web hotshot, you probably don't put your CSS selectors in the @code{<head>} tag. Instead, you link to an external CSS file. So it will not surprise you that in Pollen, you can do this by adding the usual @code{<link>} tag to your HTML template, in this case a file called @filepath{styles.css}:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]</body>
<body>◊(->html doc)</body>
</html>
}]
Fans of hand-coded CSS, I trust you to take it from here: make your @filepath{styles.css} file, and enjoy the results.
Fans of hand-coded CSS, I trust you to take it from here: put a @filepath{styles.css} file in your project directory, and enjoy the results.
But folks who paid attention during the @seclink["first-tutorial"]{first tutorial} might be wondering ``Can we link to a dynamically generated @filepath{styles.css.pp} file?''
Yes, of course. Here's the rule of thumb: when you're making links between files — whether CSS, or HTML, or anything else — Pollen doesn't care whether the file is static or dynamic. You just refer to it by its ultimate name, in this case @filepath{styles.css}. If a static @filepath{styles.css} file exists, Pollen will use that. If it doesn't, Pollen will look for a source file it can use to make @filepath{styles.css}, and generate it on the spot. (You can also start with a static file, and change it to be dynamic later, and Pollen will do the right thing.)
Yes, of course. Here's the rule of thumb: when you're making links between files — whether CSS, HTML, or anything else — Pollen doesn't care whether the file is static or dynamic. You just refer to it by its ultimate name, in this case @filepath{styles.css}. If a static @filepath{styles.css} file exists, Pollen will use that. If it doesn't, Pollen will look for a source file it can use to make @filepath{styles.css}, and generate it on the spot. (You can also start with a static file, and change it to be dynamic later, and Pollen will do the right thing.)
So to use a dynamic CSS file, we don't need to make any changes to @filepath{template.html}. We just need to add @filepath{styles.css.pp} to the project directory:
So to use a dynamic CSS file, we don't need to make any changes to @filepath{template.html.p}. We just need to add @filepath{styles.css.pp} to the project directory:
@fileblock["styles.css.pp"
@codeblock{
@ -513,12 +554,13 @@ h1 {background: ◊|h1-color|; color: white;}
strong {color: ◊|strong-color|;}
}]
Now, when you refresh @filepath{article.html} in the project server, Pollen will generate the @filepath{styles.css} file it needs, and you'll see the new colors in the page. As before, if you update @filepath{styles.css.pp}, Pollen will notice and regenerate the CSS file when you refresh the page.
This time, when you refresh @filepath{article.html} in the project server, Pollen will run @filepath{styles.css.pp} to get the @filepath{styles.css} file it needs, and you'll see the new colors in the page. As usual, if you update @filepath{styles.css.pp}, Pollen will notice and regenerate the CSS file when you refresh the page.
Can you add multiple dynamic style sheets? Yes.
@(linebreak)Can you mix dynamic and static style sheets? Yes.
@(linebreak)Can you add a dynamic JavaScript file? Yes.
@(linebreak)You're getting the general idea, right? So let's move on.
You've got the general idea, right? So let's move on.
@section[#:tag-prefix "tutorial-2"]{Intermission}
@ -531,18 +573,17 @@ If you want to create a multi-page article, however, you need to get through one
A @italic{pagetree} is a hierarchical list of Pollen pages. When you have multiple pages in your project, the pagetree establishes relationships among those pages. At its most basic, a pagetree establishes a linear sequence for the pages. But pagetrees can also establish hierarchical relationships — for instance, a book-length project can be organized into chapters, the chapters into sections, and so on. The pagetree doesn't impose any semantics on the organization of your project. It's just a tree, and it's up to you how many layers to establish, what those layers mean, and so on.
@margin-note{@italic{Pagemap} might've been an equally good name, and perhaps more consistent with similar concepts in other web-publishing systems. But I avoided it out of deference to Racket's @racket[map] function, which means something completely different.}
@subsection{Pagetree navigation}
Pagetrees are used in various ways throughout Pollen. But the most obvious use for a pagetree is to add navigational links to your pages. Obviously, in a multi-page article, readers need a way of getting from one page to the next. In this part of the tutorial, we'll expand our sample article from one page to three, and see how to create ``previous page'' and ``next page'' links in our template that are dynamically generated relative to the current page.
An obvious use for a pagetree is to add navigational links to your pages. Obviously, in a multi-page article, readers need a way of getting from one page to the next. In this part of the tutorial, we'll expand our sample article from one page to three, and see how to create ``previous page'' and ``next page'' links in our template that are dynamically generated relative to the current page.
@subsection{Using the automatic pagetree}
You've actually already been exposed to pagetrees (though I didn't tell you about it at the time). Recall that the dashboard of the project server is located at @tt{http://localhost:8080/index.ptree}. The list of files you see in the dashboard is a pagetree that Pollen creates by reading the files in the current directory and arranging them in alphabetical order.
You've actually already been exposed to pagetrees (though I didn't tell you about it at the time). Recall that the dashboard of the project server is located at @link-tt{http://localhost:8080/index.ptree}. The list of files you see in the dashboard is a pagetree that Pollen generates by getting a list of files in the current directory and arranging them in alphabetical order.
If the multiple pages in your project are already ordered by filename, then you can rely on this automatic pagetree.
Thus, if the multiple pages in your project are already ordered alphabetically by filename, then you can rely on this automatic pagetree. (More commonly, you'll make a separate pagetree file for the navigation — but we'll cover that later in this tutorial.)
From earlier in the tutorial, you have a Markdown source file called @filepath{article.html.pmd} that looks like this:
@ -580,20 +621,20 @@ The terrific third part.
As before, you can fill these source files with any sample Markdown content you like. Moreover, you don't have to use the filenames @filepath{barticle.html.pmd} and @filepath{carticle.html.pmd} — the point is that the intended sequence needs to match the alphabetic sorting of the filenames.
We'll reuse the @filepath{template.html} and @filepath{styles.css} files from earlier in the tutorial. Move or delete the other tutorial files so that your dashboard in the project server shows only these five files:
We'll reuse the @filepath{template.html.p} and @filepath{styles.css.pp} files from earlier in the tutorial. Move or delete the other tutorial files so that your dashboard in the project server shows only these five files:
@itemlist[
@item{@filepath{article.html.pmd}}
@item{@filepath{barticle.html.pmd}}
@item{@filepath{carticle.html.pmd}}
@item{@filepath{styles.css} (or @filepath{styles.css.pp})}
@item{@filepath{template.html}}
@item{@filepath{styles.css.pp}}
@item{@filepath{template.html.p}}
]
If you click on any of the three Markdown sources, you will see it converted into HTML using @filepath{template.html}, with styles appiled from @filepath{styles.css}.
If you click on any of the three Markdown sources, you'll see it converted into HTML using @filepath{template.html.p}, with styles from the generated @filepath{styles.css}.
The automatic pagetree for this project is exactly what you see in the dashboard: a list of the three article files, followed by @filepath{styles.css} and @filepath{template.html}.
The automatic pagetree for this project is exactly what you see in the dashboard: a list of the three article files, followed by @filepath{styles.css} and @filepath{template.html.p}.
@subsection{Adding navigation links to the template with @tt{here}}
@ -603,35 +644,35 @@ To make any navigation link — up, down, sideways — the general idea is that
First, let's just see @code{here} on its own. Update your template as follows:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]
<body>◊(->html doc)
The current page is called ◊|here|.
</body>
</html>
}]
If you refresh @filepath{article.html}, you will now see the line ``The current page is called article.html.'' Switch to @filepath{barticle.html}, and you'll see ``The current page is called barticle.html.'' Makes sense, right?
If you refresh @filepath{article.html}, you'll now see the line ``The current page is called article.html.'' Switch to @filepath{barticle.html}, and you'll see ``The current page is called barticle.html.'' Makes sense, right?
Now let's use pagetree functions to show the names of the previous and next pages. Consistent with the usual Pollen policy of obviousness, these functions are called @racket[previous] and @racket[next]:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]
<body>◊(->html doc)
The current page is called ◊|here|.
The previous is ◊|(previous here)|.
The next is ◊|(next here)|.
@ -641,18 +682,18 @@ The next is ◊|(next here)|.
Refresh @filepath{barticle.html}. You'll now see that ``The current page is called barticle.html. The previous is article.html. The next is carticle.html.'' So far, so good: we're correctly deriving the previous and next pages from the automatic pagetree.
All that's left is to add hyperlinks, which is easy:
All that's left is adding the hyperlinks:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]
<body>◊(->html doc)
The current page is called ◊|here|.
The previous is <a href="◊|(previous here)|">◊|(previous here)|</a>.
The next is <a href="◊|(next here)|">◊|(next here)|</a>.
@ -670,20 +711,20 @@ If you clicked through to @filepath{article.html} or @filepath{carticle.html}, y
One way to fix the problem would be to have three separate template files — the standard one with both previous- and next-page links, one with only a next-page link, and one with only a previous-page link.
But since we have a whole programming language available in Pollen, that's a dull-witted way to solve the problem. The better way is to add @italic{conditionals} to the template to selectively change the navigation. That keeps things simple, because we'll still have only one @filepath{template.html} to deal with.
But since we have a whole programming language available in Pollen, that's a weak solution. The better way is to add @defterm{conditionals} to the template to selectively change the navigation. That keeps things simple, because we'll still have only one @filepath{template.html.p} to deal with.
To handle @filepath{article.html}, we want to hide the previous-page navigation link when there's no previous page. As it turns out, if the @racket[previous] function can't find a previous page, it will return false. So we just need to wrap our previous-page navigation in the @racket[when/splice] command like so:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]
<body>◊(->html doc)
The current page is called ◊|here|.
◊when/splice[(previous here)]{The previous is
<a href="◊|(previous here)|">◊|(previous here)|</a>.}
@ -692,20 +733,24 @@ The next is <a href="◊|(next here)|">◊|(next here)|</a>.
</html>
}]
The basic structure of @racket[when/splice] is @tt{◊when/splice[@racketvarfont{condition}]{@racketvarfont{insert-this-text}}.} Note the square braces around the @racketvarfont{condition}, and the curly braces around the @racketvarfont{text}. Using @racket[(previous here)] as the condition is shorthand for ``when @racket[(previous here)] does not return false...''
The basic form of @racket[when/splice] is
@racketfont{◊when/splice[@racketvarfont{test-condition}]{@racketvarfont{content-to-insert}}}
We're writing this command in Pollen style — note the square braces around the @racketvarfont{condition}, and the curly braces around the @racketvarfont{text}. Using @racket[(previous here)] as the condition is shorthand for ``when @racket[(previous here)] does not return false...''
Programmers in the audience might be getting anxious about the repeated use of @racket[(previous here)] — you're welcome to store that value in a variable, and everything will work the same way:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]
<body>◊(->html doc)
The current page is called ◊|here|.
◊(define prev-page (previous here))
◊when/splice[prev-page]{The previous is
@ -717,18 +762,18 @@ The next is <a href="◊|(next here)|">◊|(next here)|</a>.
We need a different technique for handling the end of the next-page navigation, because we're not reaching the actual end of the pagetree. We're just reaching the end of the pages we care about navigating through.
What condition will help us detect this? Here, we can notice that the names of our article pages all contain the string @code{article}. While you'd probably want a more robust condition for a real project, in this tutorial, what we'll do is hide the next-page navigation if the name of the next page doesn't contain ``@code{article}''. As we did before, we wrap our navigation line in the @racket[when/splice] function:
What condition will help us detect this? This time, we might notice that the names of our article pages all contain the string @code{article}. In a real project, you'd probably want a more meaningful test condition. But in this tutorial, what we'll do is hide the next-page navigation if the name of the next page doesn't contain @filepath{article}. As we did before, we wrap our navigation line in the @racket[when/splice] function:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]
<body>◊(->html doc)
The current page is called ◊|here|.
◊(define prev-page (previous here))
◊when/splice[prev-page]{The previous is
@ -739,15 +784,19 @@ The next is <a href="◊|(next here)|">◊|(next here)|</a>.}
</html>
}]
This time, the condition is @racket[(regexp-match "article" (symbol->string (next here)))]. How were you supposed to know this? You weren't. That's why this is a tutorial. Without going on a lengthy detour, the @racket[regexp-match] function returns true if the first string (in this case, @racket["article"]) is found inside the second string (in this case, we convert @racket[(next here)] to a string by wrapping it in @racket[symbol->string]).
This time, the condition is @racket[(regexp-match "article" (symbol->string (next here)))]. How were you supposed to know this? You weren't. That's why this is a tutorial. Without going on a lengthy detour, the @racket[regexp-match] function is Racket's regular-expression matcher. It returns true if the first string (in this case, @racket["article"]) is found inside the second string (in this case, we convert @racket[(next here)] to a string by wrapping it in @racket[symbol->string]).
Even if some of the programmy bits went over your head just now, relax and paste the code into your template. What you'll see when you refresh @filepath{carticle.html} is that the next-page link is gone. So now our template lets us navigate among the pages of our article, and the conditionals handle the end pages correctly.
In any case, even if some of the programmy bits went over your head just now, relax and paste the code into your template. What you'll see when you refresh @filepath{carticle.html} is that the next-page link is gone. So now our template lets us navigate among the pages of our article, and the conditionals handle the end pages correctly.
@subsection{Making a pagetree file}
@subsection{Making a pagetree source file}
I didn't want to dwell on programming complications in the previous section. Why? Because the extra programming was necessary only because we made life somewhat difficult for ourselves by relying on the automatic pagetree.
I didn't want to dwell on programming complications in the last conditional. Why? The extra programming was necessary only because we made life somewhat difficult for ourselves by relying on the automatic pagetree. A better way to solve the problem is to avoid it altogether by making a pagetree file.
A better way to solve the problem is to avoid it altogether by making a pagetree file.
Pagetree source files have a different syntax and status than other Pollen source files, so they are parsed using their own Pollen dialect. To invoke this dialect, you just start the file with @tt{#lang pollen} and name the file with the @filepath{ptree} extension, for instance @filepath{my-project.ptree}. While you can have as many pagetrees in your project as you want, Pollen will first look for one named @filepath{index.ptree}.
Pagetree files have a different syntax and status than other Pollen source files, so they're compiled using their own Pollen dialect. To invoke this dialect, you just start the file with @tt{#lang pollen} and name the file with the @filepath{ptree} extension, for instance @filepath{my-project.ptree}. While you can have as many pagetrees in your project as you want, Pollen will first look for one named @filepath{index.ptree}.
@margin-note{A pagetree file only gets one file extension, because unlike other Pollen source files, it's never converted into an output file.}
So let's make an @filepath{index.ptree} file. At its simplest, a pagetree file can just be a list of files in the intended order. In DrRacket, create a new file in your project directory as follows:
@ -764,9 +813,9 @@ Now run the file. The result will be:
@repl-output{'(pagetree-root carticle.html article.html barticle.html)}
Pretty boring, I know. But behind the scenes, Pollen's pagetree parser is making sure your tree is valid (e.g., no duplicate or malformed names). Today it's boring, but on the day you have a long and complicated pagetree, you will be grateful.
Pretty boring, I know. But behind the scenes, Pollen's pagetree compiler is making sure your tree is valid (e.g., no duplicate or malformed names). Today it's boring, but on the day you have a long and complicated pagetree, you'll be grateful.
Notice that the names in this pagetree are the names of @italic{output} files, not source files. This is deliberate, so that neither you nor Pollen has to care which files are static vs. dynamic. This next pagetree wouldn't be wrong in the sense of bad syntax — the pagetree parser won't complain — but it would be wrong in the sense of not-what-you-want, because it refers to source names rather than output names:
Notice that the names in this pagetree are the names of @italic{output} files, not source files. This is deliberate. Neither you nor Pollen has to care which files are static vs. dynamic. This next pagetree wouldn't be wrong in the sense of bad syntax — the pagetree compiler won't complain — but it would be wrong in the sense of not-what-you-want, because it refers to source names rather than output names:
@fileblock["bad-index.ptree"
@codeblock{
@ -779,31 +828,35 @@ barticle.html.pmd
You also probably noticed that the files are in a different order than they were in the automatic pagetree: @filepath{carticle.html} is first, followed by @filepath{article.html} and then @filepath{barticle.html}. This too is deliberate, so we can see what happens with a differently ordered pagetree.
Pagetrees don't change as often as other source files, so as a performance optimization, the project server doesn't automatically reload pagetrees when they change. To trigger a reload of the pagetree, you have two options. You can either go to your terminal window and stop the project server with @onscreen{Ctrl+C}, and then restart it. Or, if you make a change to a source file that relies on the pagetree (in this case, one of the @filepath{pmd} source files), the pagetree will be reloaded.
Pagetrees don't change as often as other source files, so as a performance optimization, the project server ignores them when deciding whether to refresh a file. Thus, after updating a pagetree, you have to trigger a refresh manually. The easiest way is to restart the project server — go to your terminal window, stop the project server with @onscreen{ctrl+C}, and then restart it.
Now refresh @filepath{carticle.html}. You'll notice that the navigation links are different. You won't see a previous-page link — because @filepath{carticle.html} is now the first page in the pagetree — and the next page will show up as @filepath{article.html}. Click through to @filepath{article.html}, and you'll see the navigation likewise updated. Click through to @filepath{barticle.html}, and you'll see ...
BAM! An error page that says @tt{Cant convert #f to string}. What happened? We switched to using our own pagetree file but we didn't update our template conditionals. Once you reach @filepath{barticle.html}, the value of @racket[(next here)] is false, which means the @racket[(symbol->string (next here))] command in the template conditional is trying to convert false into a string. Hence the error.
BAM! An error page that says
@errorblock{symbol->string: contract violation
expected: symbol?
given: #f}
What happened? We switched to using our own pagetree file but we didn't update the conditions in our template. Once we reach @filepath{barticle.html}, the value of @racket[(next here)] is false (@racket[#f]). But the @racket[(symbol->string (next here))] command in the template needs a symbol as input. Hence the error.
So let's go back and fix that. Because we don't have extraneous files in our pagetree anymore, we can change the second conditional in the template to work the same way as the first:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
<head>
<meta charset="UTF-8">
<title>◊select['h1 doc], by MB</title>
<link rel="stylesheet" type="text/css" media="all" href="styles.css" />
<meta charset="UTF-8">
<title>◊(select 'h1 doc), by MB</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>◊->html[doc]
<body>◊(->html doc)
The current page is called ◊|here|.
◊(define prev-page (previous here))
◊when/splice[prev-page]{The previous is
<a href="◊|prev-page|">◊|prev-page|</a>.}
◊when/splice[prev-page]{The previous is <a href="◊|prev-page|">◊|prev-page|</a>.}
◊(define next-page (next here))
◊when/splice[next-page]{
The next is <a href="◊|next-page|">◊|next-page|</a>.}
◊when/splice[next-page]{The next is <a href="◊|next-page|">◊|next-page|</a>.}
</body>
</html>
}]
@ -812,15 +865,15 @@ Refresh @filepath{barticle.html} — because you're updating the template, you d
@subsection{@tt{index.ptree} & the project server}
One more thing to show you before we wrap up this tutorial. Remember that the dashboard of the project server is at @tt{http://localhost:8080/index.ptree}? By default, the project server will synthesize a pagetree from an alphbetical directory listing.
One more thing before we wrap up this tutorial. Remember that the dashboard of the project server is at @link-tt{http://localhost:8080/index.ptree}? By default, the project server will generate a pagetree from an alphbetical directory listing.
But if you put your own @filepath{index.ptree} file in the directory, the project server will use that for the dashboard instead. In fact, visit @link-tt{http://localhost:8080/index.ptree} now and you'll see what I mean. Consistent with the @filepath{index.ptree} you made, you'll now see @filepath{carticle.html}, @filepath{article.html}, and @filepath{barticle.html}, but not @filepath{template.html} nor @filepath{styles.css} (even though they're still in the project directory).
But if you put your own @filepath{index.ptree} file in that directory, the project server will use that for the dashboard instead. In fact, visit @link-tt{http://localhost:8080/index.ptree} now and you'll see what I mean. Consistent with the @filepath{index.ptree} you made, you'll now see @filepath{carticle.html}, @filepath{article.html}, and @filepath{barticle.html}, but not @filepath{template.html.p} nor @filepath{styles.css} (even though they're still in the project directory).
@section{Second tutorial complete}
That was a big tutorial. I commend you for your tenacity and patience. But in this tutorial, you made a giant leap forward. Despite the silly examples, you now know everything you need to make multi-page articles — books, even — using Markdown authoring mode in Pollen. If this is all you ever use Pollen for, it'll be a big improvement over ordinary Markdown.
That was a big tutorial. I commend you for your tenacity and patience. But you made a giant leap forward. Despite the silly examples, you now know everything you need to make multi-page articles — books, even — using Markdown authoring mode in Pollen. If this is all you ever use Pollen for, it'll be a big improvement over ordinary Markdown.
But there's more. We haven't even gotten into the more elaborate automation that's possible with Pollen, nor Pollen's own markup language. We'll cover that in the third tutorial.
But there's more. We haven't even gotten into the more elaborate automation that's possible with Pollen, nor Pollen's own markup language. We'll cover that in the @seclink["third-tutorial"]{third tutorial}.

@ -1,6 +1,6 @@
#lang scribble/manual
@(require scribble/eval (for-label pollen/decode plot pollen/setup pollen/tag racket/base pollen/template txexpr racket/list racket/string))
@(require scribble/eval (for-label pollen/decode pollen/core racket/math plot pollen/setup pollen/tag racket/base pollen/template txexpr racket/list racket/string))
@(require "mb-tools.rkt")
@(define my-eval (make-base-eval))
@ -36,13 +36,13 @@ I'll assume you've completed the @seclink["second-tutorial"]{second tutorial} an
Because now it's time to pick up the pace. You've learned how to do some handy things with Pollen. But we haven't yet exploited the full fusion of writing environment and programming language. I promised you that @secref["the-book-is-a-program"], right? So let's do some programming.
@section{Pollen markup vs. XML}
@section[#:tag "pollen-vs-xml"]{Optional reading: Pollen markup vs. XML}
You can skip this section if XML holds no interest. But Pollen markup evolved out of my attempt to come up with an alternative to XML that would be more usable for writing. So if you're familiar with XML, the contrast may be helpful.
@subsection{The XML problem}
In the @seclink["second-tutorial"]{second tutorial}, I made the case that Markdown is a limiting format for authors. Why? Markdown is essentially a notation system for HTML tags. As such, it has three problems: it's not semantic, it only covers a limited subset of HTML tags, and it can't be extended by an author.
In the @seclink["second-tutorial"]{second tutorial}, I argued that Markdown is a limiting format for authors. Why? Because Markdown is merely shorthand notation for HTML tags. As such, it has three problems: it's not semantic, it only covers a limited subset of HTML tags, and it can't be extended by an author.
These problems are partly limitations of HTML itself. And these limitations were meant to be cured by XML — the @italic{X} stands for @italic{extensible}. In principle, XML allows you to define whatever tags you like and use them in your document.
@ -52,13 +52,13 @@ So why hasn't XML taken over the world? In practice, XML promises more than it d
@item{@bold{Verbose syntax}. Unfortunately, XML relies on the same angle-bracket notation as HTML. If you think HTML source is hard to read, XML is worse. Since much of writing involves reading, this feature is also a major bug.}
@item{@bold{Validation overhead}. Integral to XML is the concept of @defterm{validation}, which guarantees that a document meets certain formal criteria, usually asserted in a @italic{schema}. To get the full value from XML, you generally want to use validation. But doing so imposes a lot more work on you as an author, and removes much of the expressive potential of XML.}
@item{@bold{Validation overhead}. Integral to XML is the concept of @defterm{validation}, which guarantees that a document meets certain formal criteria, usually defined in a @italic{schema}. To get the full value from XML, you generally want to use validation. But doing so imposes a lot more work on you as an author, and removes much of the expressive potential of XML.}
@item{@bold{Masochistic document processing}. I'm referring to XSLT, the preferred method of transforming XML documents. I know a little XSLT, so I'll concede that there's a method to its madness. But it's still madness.}
]
The nicest thing we could say about XML is that its intentions are good. It's oriented toward the right goals. But its benefits are buried under atrocious ergonomics.
The nicest thing we could say about XML is that its intentions are good. It's pointed toward the right goals. But its benefits are buried under atrocious ergonomics.
@subsection{What Pollen markup does differently}
@ -86,7 +86,7 @@ In this tutorial, I'll be rendering Pollen markup with an HTML template. But you
@section{Writing with Pollen markup}
Pollen markup is a free-form markup system that lets you add arbitrary @defterm{tags} and @defterm{attributes} to your text. By arbitrary, I mean that they don't need to match up with an existing schema or specification (e.g., the tags permitted by HTML). They can — but that's an option, not a requirement.
Pollen markup is a free-form markup system that lets you add arbitrary @defterm{tags} and @defterm{attributes} to your text. By arbitrary, I mean that you needn't constrain your tags to an existing specification (e.g., the tags permitted by HTML). You can — but that's an option, not a requirement.
I like to think of Pollen markup a way of capturing not just the text, but also my @bold{ideas about the text}. Some of these are low-level ideas (``this text should be italicized''). Some are high-level ideas (``this text is the topic of the page''). Some are just notes to myself. In short, everything I know about the text becomes part of the text.
@ -94,9 +94,9 @@ In so doing, Pollen markup becomes the source code of the book. Let's try it out
@subsection{Creating a Pollen markup file}
We're going to use Pollen markup to make a file that will ultimately be HTML. So consistent with the authoring-mode workflow we learned in the @seclink["second-tutorial"]{second tutorial}, we'll start with our desired output filename, @filepath{article.html}, and then append the Pollen markup suffix, @filepath{.pm}.
We're going to use Pollen markup to make a source file that will ultimately become HTML. So consistent with the authoring-mode workflow we learned in the @seclink["second-tutorial"]{second tutorial}, we'll start with our desired output filename, @filepath{article.html}, and then append the new Pollen markup suffix, which is @filepath{.pm}.
In DrRacket, start a new file called @filepath{article.html.pm} like so (BTW you can use any sample text you like):
In DrRacket, start a new file called @filepath{article.html.pm} like so (as usual, you can use any sample text you like):
@fileblock["article.html.pm" @codeblock{
#lang pollen
@ -108,13 +108,15 @@ Consistent with usual authoring-mode policy, when you run this file, you'll get
@repl-output{'(root "I want to attend RacketCon this year.")}
Remember, even though the first line of the file is @racketmodfont{#lang} @racketmodname[pollen] — same as the last tutorial — the new @filepath{.pm} suffix signals that Pollen should interpret the source as Pollen markup. Look what happens if you goof up and put Markdown source in a Pollen markup file, like so:
Remember, even though the first line of the file is @racketmodfont{#lang} @racketmodname[pollen] — same as the last tutorial — the new @filepath{.pm} suffix signals that Pollen should interpret the source as Pollen markup.
@codeblock{
For instance, look what happens if you goof up and put Markdown source in a Pollen markup file, like so:
@fileblock["article.html.pm" @codeblock{
#lang pollen
I am **so** excited to attend __RacketCon__ this year.
}
}]
The Markdown syntax will be ignored, and pass through to the output:
@ -125,15 +127,16 @@ Restore the non-Markdown source, and let's continue.
@subsection{Tags & tag functions}
Pollen markup uses the same Pollen command syntax that we first saw in @secref["Adding_commands"]. Previously, we used this command syntax to invoke functions like @racket[define] and @racket[->html]. Pollen markup is used to invoke a special kind of function called a @defterm{tag function}, which is a function that, by default, adds a tag to the text.
Pollen markup uses the same Pollen command syntax that we first saw in @secref["Adding_Pollen_commands"]. Previously, we used this syntax to invoke functions like @racket[define] and @racket[->html]. This consistency in syntax is deliberate, because Pollen markup is used to invoke a special kind of function called a @defterm{tag function}, which is a function that, by default, adds a tag to the text.
To see how this works, restore your @filepath{article.html.pm} file to its original state:
@codeblock{
@fileblock["article.html.pm" @codeblock{
#lang pollen
I want to attend RacketCon this year.
}
}]
We can add any tag with Pollen markup, but for now, let's start with an old favorite: @code{em}, which is used in HTML to add emphasis to text. We apply a tag by starting with the lozenge character (◊) followed by the tag name @code{em}, followed by the text in curly braces, like so:
@ -148,7 +151,7 @@ Run this file in DrRacket and see the X-expression that results:
@repl-output{'(root "I want to attend " (em "RacketCon this year") ".")}
You won't be surprised to hear that you can nest tags:
You won't be surprised to hear that you can nest tags within each other:
@fileblock["article.html.pm" @codeblock{
#lang pollen
@ -163,17 +166,17 @@ With the expected results:
@defterm{Attributes} are like tags for tags. Each attribute is a keyvalue pair where the key is any name, and the value is a string. Anyone who's seen HTML is familiar with them:
@repl-output{<span class="author">Prof. Leonard</span>}
@terminal{<span class="author">Prof. Leonard</span>}
Here, @code{class} is an attribute for @code{span} that has value @code{"author"}. And this is what it looks like as an X-expression:
@repl-output{'(span ((class "author")) "Prof. Leonard")}
You can add any number of attributes to a tag (first as an X-expression, then as HTML):
You can add any number of attributes to a tag (first as HTML, then as an X-expression):
@repl-output{'(span ((class "author")(id "primary")(living "true")) "Prof. Leonard")}
@terminal{<span class="author" id="primary" living="true">Prof. Leonard</span>}
@repl-output{<span class="author" id="primary" living="true">Prof. Leonard</span>}
@repl-output{'(span ((class "author")(id "primary")(living "true")) "Prof. Leonard")}
In Pollen markup, attributes have the same logic, but a slightly different syntax. In keeping with the tag notation you just saw, the @code{span} tag is added in the usual way:
@ -192,7 +195,7 @@ Then you have two options for adding attributes. The verbose way corresponds to
Each keyvalue pair is in parentheses, and then the list of pairs is within parentheses, with a @racket[quote] (@litchar{'}) at the front that signals that the text should be used literally.
This involves some superfluous typing, however, so Pollen also allows you to specify attributes with keyword arguments:
But this is boring to type out, so Pollen also allows you to specify attributes with Racket-style @seclink["keyword-args" #:doc '(lib "scribblings/guide/guide.scrbl")]{keyword arguments}:
@fileblock["article.html.pm" @codeblock{
#lang pollen
@ -209,25 +212,25 @@ Both of these forms will produce the same X-expression:
Now that you know how to make tags and attributes, you might wonder whether Pollen markup can be used as a quick & dirty HTML-notation system. Sure — for a quick & dirty project, why not. Recall that @secref["X-expressions"] are just alternative notation for the standard angle-bracket notation used in HTML. So if you wanted HTML like this:
@repl-output{<div class="red" style="font-size:150%">Important <em>News</em></div>}
@terminal{<div class="red" style="font-size:150%">Important <em>News</em></div>}
You could write it in Pollen markup like so:
@repl-output{◊div[#:class "red" #:style "font-size:150%"]{Important ◊em{News}}}
@code{◊div[#:class "red" #:style "font-size:150%"]{Important ◊em{News}}}
And then just convert it (using the @racket[->html] function) into the HTML above. Thus, the tags you already know (and love?) can be used in Pollen markup, but with fewer keystrokes and cruft.
And then convert it (using the @racket[->html] function) into the HTML above. Thus, the tags you already know (and love?) can be used in Pollen markup, but with fewer keystrokes and cruft.
Still, if Pollen markup were just an alternative notation system for HTML tags, it would be pretty boring. As I alluded above, that's merely a boring way to use it.
Still, if Pollen markup were just an alternative notation system for HTML tags, it would be pretty boring. As I alluded above, that's merely the simplest way to use it.
In the XML spirit, Pollen markup lets you use any tags you want. That's considerably less boring.
@subsection{What are custom tags good for?}
@subsection{Optional reading: What are custom tags good for?}
XML jocks can skip this section, since you already know. But if you've been mired in Markdown or HTML, read on.
XML jocks can skip this section, since you already know. But if you've been living in the Markdown / HTML lowlands, read on.
Tags, broadly speaking, are a means of annotating a text with extra information, which I'll call @defterm{metadata} (using that term in its generic sense, not in any fiddly computery way). Metadata is the key tool that enables an author to write a book with the benefits of @defterm{semantic markup} and @defterm{format independence}.
@subsection{Semantic markup}
@subsubsection{Semantic markup}
@defterm{Semantic markup} means adding metadata to text according to the meaning of the text, not merely its intended visual appearance. So rather than tagging @code{RacketCon} with an @code{em} tag, as we did above to indicate how the word should look, maybe we would tag it with an @code{event} tag, to indicate what @italic{kind} of thing it is.
@ -235,7 +238,7 @@ Semantic markup lets an author specify distinctions that would be ambiguous in p
This has two major benefits. First, by separating appearance and meaning, an author can manage the content of the book in useful ways. For instance, if every movie title were tagged as @code{movie-title} rather than @code{italic}, then it would be simple to generate a list of all movies mentioned in the book (for the author's benefit) or a page index of movie references (for the reader's benefit). But without that semantic tagging, a movie title couldn't be distinguished from any other italicized text.
@subsection{Format independence}
@subsubsection{Format independence}
The second benefit of custom tags is @defterm{format independence}, or the ability to change the rendering of the text to suit a particular device or context.
@ -249,13 +252,13 @@ Using a display-driven model to manage this complexity is a terrible idea — as
This isn't surprising. For a long time, text processing has been dominated by this display-driven model. Most word processors, like Microsoft Word and Pages, have been built around this model. It worked well enough in the era where most documents were eventually going to be printed on paper (or a paper simulator like PDF). HTML was a technical leap forward, but not a conceptual leap: it mostly represented the display options available in a web browser.
@margin-note{There's a couple TeX fans at the back of the room, waving their arms. Yes, TeX got a lot of things right. In practice, however, it never became a core tool for electronic publishing (which, to be fair, didn't exist when TeX was written). Plenty of ideas in Pollen were lifted from TeX.}
@margin-note{There's a couple TeX fans at the back of the room, waving their arms. Yes, TeX got a lot of things right. In practice, however, it never became a core tool for electronic publishing (which, to be fair, didn't exist when TeX was written). But plenty of ideas in Pollen have been lifted from TeX.}
For a document to be format independent, two conditions have to be satisfied.
First, the document has to be readable by other programs, so they can handle the conversion of format-independent markup into a format-specific rendering (e.g., mapping semantic tags like @code{movie-title} onto visual tags like @code{em}). Most word-processor formats, like Word's .docx, are bad for authoring because these formats are opaque and proprietary. We needn't get into the political objections. As a practical matter, they're inarguably restrictive — if you can't get your data out of your file, you're stuck.
First, the document has to be readable by other programs, so they can handle the conversion of format-independent markup into a format-specific rendering (e.g., mapping semantic tags like @code{movie-title} onto visual tags like @code{em}). Most word-processor formats, like Word's @code{.docx}, are bad for authoring because these formats are opaque and proprietary. We needn't get into the political objections. As a practical matter, they're inarguably restrictive — if you can't get your data out of your file, you're stuck.
Second, the document itself has to be represented in a way that's independent of the particularities of any one format. For instance, HTML is a bad authoring format because it encourages authors to litter their text with HTML-isms like @code{h1} and @code{span}. These have no meaning outside of HTML, and thus will always cause conversion problems. The @seclink["The_case_against_Markdown"]{same goes for Markdown}, which is simply HTML in disguise.
Second, the document itself has to be represented in a way that's independent of the particularities of any one format. For instance, HTML is a bad authoring format because it encourages authors to litter their text with HTML-isms like @code{h1} and @code{span}. These have no meaning outside of HTML, and thus will always cause conversion problems. The @seclink["the-case-against-markdown"]{same goes for Markdown}, which is simply HTML in disguise.
@ -275,37 +278,37 @@ This markup will turn into this X-expression:
@repl-output{'(root "I want to attend " (event "RacketCon") " this year.")}
Which is equivalent to this XML:
Which is equivalent to this HTML-ish markup:
@repl-output{<root>I want to attend <event>RacketCon</event> this year.</root>}
@terminal{<root>I want to attend <event>RacketCon</event> this year.</root>}
In truth, Pollen doesn't notice any difference between a custom tag vs. a standard HTML tag vs. any other kind of tag. They're all just markup tags. If you want to restrict yourself to a certain vocabulary of tags, you can. If you want to set up Pollen to enforce those restrictions, you can do that too. But by default, Pollen doesn't impose restrictions like this. In general, you can pick any tag name you want, and it will work.
In truth, Pollen doesn't notice the differences among a custom tag, a standard HTML tag, or any other kind of tag. They're all just markup tags. If you want to restrict yourself to a certain vocabulary of tags, you can. If you want to set up Pollen to enforce those restrictions, you can do that too. But by default, Pollen doesn't impose restrictions like this. In general, you can pick any tag name you want, and it will work.
Don't take my word for it. See what happens if you write this:
Don't take my word for it. See what happens when you write this and run it:
@fileblock["article.html.pm" @codeblock{
#lang pollen
I want to attend ◊verylongandimpracticaltagname{RacketCon} this year.}]
I want to attend ◊long-and-impractical-tag-name{RacketCon} this year.}]
One small but important exception to this rule. If you were wondering why I sometimes call them @defterm{tag functions} instead of just @defterm{tags}, it's because under the hood, every tag is implemented as a function. The default behavior of this function is just to wrap the text in a tag with the given name.
The benefit of treating tags as functions will become evident later in this tutorial. But the cost of this approach is that tags occupy the same namespace as the other functions available in Pollen (and by extension, Racket). So if you try to use a tag name that's already the name of an existing function, an error will occur.
The benefit of treating tags as functions will become evident later in this tutorial. But the cost of this approach is that tags occupy the same namespace as the other functions available in Pollen (and by extension, Racket). Meaning, if you try to use a tag name that's already being used for an existing function, you'll get an error.
For instance, let's suppose you try to use a custom tag called @code{length}:
For instance, suppose we try to use a custom tag called @code{length}:
@fileblock["article.html.pm" @codeblock{
#lang pollen
The Panama Canal is ◊length{77km} across.}]
When you run this file, you get an error:
When we run this file, we get an error:
@errorblock{length: contract violation;
expected: list?
  expected: list?
  given: "77km"}
The problem is that Racket already provides a function called @racket[length]. Consistent with the usual rules of Pollen command notation, your command is interpreted as an attempt to invoke the @racket[length] function, rather than apply a tag named @code{length}.
The problem is that Racket already has a function called @racket[length]. Consistent with the usual rules of Pollen command notation, your command is interpreted as an attempt to invoke the @racket[length] function, rather than apply a tag named @tt{length}.
In practice, namespace clashes are rare. But if necessary, they're easy to work around (for the simplest method, see @secref["Invoking_tag_functions"]).
@ -358,13 +361,15 @@ Leading us to the Three Golden Rules of Pollen Tags:
@item{@bold{Every Pollen tag calls a function with the same name.}}
@item{@bold{The input values for that function are the attributes and content of the tag.}}
@item{@bold{The input values for that function are the attributes and elements of the tag.}}
@item{@bold{The whole tag — tag name, attributes, and content — is replaced with the return value of the called function.}}
@item{@bold{The whole tag — tag name, attributes, and elements — is replaced with the return value of the called function.}}
]
Corollary to rule #3: because a tag represents a single X-expression, a tag function must also return a single X-expression. If you want to return multiple elements, you'll need to wrap them in a new tag (thus grouping them into a single X-expression). (If you absolutely must splice multiple elements in place of a single X-expression, you'll need to use @racket[decode] rather than a tag function.)
Corollary to rule #3: because a tag represents a single X-expression, a tag function must also return a single X-expression. If you want to return multiple elements, you have to wrap them in a single X-expression.
@margin-note{Corollary to the corollary: you can use Pollen's special splicing operator (@racket[\@]) as the tag of your return value to hoist its elements into the containing X-expression.}
You've already seen the simplest kind of function in a Pollen document: the @seclink["Tags___tag_functions"]{default tag function}, which emulates the behavior of standard markup tags.
@ -376,7 +381,7 @@ You've already seen the simplest kind of function in a Pollen document: the @sec
I want to attend ◊em{RacketCon ◊strong{this} year}.}]
What happens when you run this source? Working from the inside out, Pollen calls the function @code{strong} with the input @code{"this"}. The result is @code{(strong "this")}. Then Pollen calls the function @code{em} with the three input values @code{"RacketCon " (strong "this") " year"}, which yields @code{(em "RacketCon " (strong "this") " year")}. Finally, Pollen calls the @code{root} function with everything in the document, resulting in:
What happens when you run this source? Working from the inside out, Pollen calls the tag function @code{strong} with the input @code{"this"}. The result is @code{(strong "this")}. Then Pollen calls the tag function @code{em} with the three input values @code{"RacketCon " (strong "this") " year"}, which yields @code{(em "RacketCon " (strong "this") " year")}. Finally, Pollen calls the tag function @code{root} with everything in the document, resulting in:
@repl-output{'(root "I want to attend " (em "RacketCon " (strong "this") " year") ".")}
@ -406,34 +411,49 @@ Sometimes this default behavior will suffice. But other times, you'll want to ch
]
How do you change the behavior of a tag? By 1) writing a new function and 2) giving it the name of the tag. Once you do this, this new behavior will automatically be invoked when you use the tag.
How do you change the behavior of a tag? Two steps:
@itemlist[#:style 'ordered
@item{Write a new function.}
@item{Give it the name of the tag.}]
For example, let's redefine the @code{strong} tag in our example above to simply print @code{BOOM}:
Once you do this, this new behavior will automatically be invoked when you use the tag.
For example, let's redefine the @code{strong} tag in our example above to simply print @racket{BOOM}:
@fileblock["article.html.pm" @codeblock{
#lang pollen
define[(strong . lines)]{BOOM}
(define (strong word) "BOOM")
I want to attend ◊em{RacketCon ◊strong{this} year}}]
When you run this file, you indeed get:
@repl-output{'(root "I want to attend " (em "RacketCon " "BOOM" " year"))}
How does this work? First, although you can define a function using either of @secref["the-two-command-modes"], it tends to be easier to use Racket mode. I wrote the first one in Pollen mode. But for clarity, I'm going to switch to Racket mode (run this file and convince yourself it comes out the same):
How does this work? Let's look at our new function definition. As usual, we start with the lozenge character (@litchar{◊}) to denote a Pollen command. Then we use @racket[define] to introduce a function definition. The name of the function comes next, which needs to match our tag name, @code{strong}. The expression @racket[(strong word)] means ``the name of this function is @racket[strong], and it takes a single word as input, which we'll refer to as @racket[word].'' Finally we have the return value, which is @racket["BOOM"].
@margin-note{This example defines the function with a Racket-style command. In this simple case, you could also use a Pollen-style command, e.g., @code{◊define[(strong word)]{BOOM}}. But in general, defining functions with Racket-style commands is more flexible.}
Let's run this file again, but go back to the Golden Rules to understand what happens. Working from the inside out:
@fileblock["article.html.pm" @codeblock{
#lang pollen
◊(define (strong word) "BOOM")
I want to attend ◊em{RacketCon ◊strong{this} year}.}]
I want to attend ◊em{RacketCon ◊strong{this} year}}]
Let's look at our new function definition. As usual, we start with the lozenge character (@litchar{◊}) to denote a Pollen command. Then we use @racket[define] to introduce a function definition. The name of the function comes next, which needs to match our tag name, @code{strong}. The expression @racket[(strong word)] means ``the name of this function is @racket[strong], and it takes a single word as input, which we'll refer to as @racket[word].'' Finally we have the return value, which is @racket["BOOM"].
@itemlist[#:style 'ordered
@item{Pollen calls the function @code{strong} with the input @code{"this"} — same as before. But this time, the result of the @racket[strong] function is not the X-expression @code{(strong "this")}, but simply @racket{BOOM}.}
@item{Then Pollen calls the function @code{em} with the three input values @code{"RacketCon " "BOOM" " year"}. Because @code{em} is still a default tag function, it yields the X-expression @code{(em "RacketCon " "BOOM" " year")}.}
@item{Finally, Pollen calls the @code{root} function with everything in the document.}
]
Let's run this file again, but go back to the Golden Rules to understand what happens. Working from the inside out, Pollen calls the function @code{strong} with the input @code{"this"} — same as before. But this time, the result of the @racket[strong] function is not @code{(strong "this")}, but simply @code{BOOM}. Then Pollen calls the function @code{em} with the three input values @code{"RacketCon " "BOOM" " year"}, which yields @code{(em "RacketCon " "BOOM" " year")}. Finally, Pollen calls the @code{root} function with everything in the document, resulting in:
The result:
@repl-output{'(root "I want to attend " (em "RacketCon " "BOOM" " year"))}
@ -455,21 +475,21 @@ Otherwise, get ready to rock.
@section{Organizing functions}
In the tag-function examples so far, we've defined each function within the source file where we used it. This is fine for quick little functions.
In the tag-function examples so far, we've defined each function within the source file where we used it. This is fine for quick little functions that are specific to a particular file.
But more often, you're going to want to use functions defined elsewhere, and store your own functions available so they're available to your source files.
But more often, you'll want to use functions available in existing code libraries, and store your own functions so they can be available to other source files.
@margin-note{For now, we're just invoking functions within a Pollen markup file. But as you'll see in the fourth tutorial, any function can be called from any kind of Pollen source file.}
@margin-note{For now, we're just invoking functions from within a Pollen markup file. But as you'll see in the @seclink["fourth-tutorial"]{fourth tutorial}, any function can be called from any kind of Pollen source file.}
@subsection{Using Racket's function libraries}
Any function in Racket's extensive libraries can be called by loading the library with the @racket[require] command, which will make all its functions and constants available with the usual Pollen command syntax:
Any function in Racket's extensive libraries can be used by loading the library with the @racket[require] command. This will make its functions and values available in the current source file with the usual Pollen command syntax. For instance, suppose we want to use the value @racket[pi] and function @racket[sinh] from @racketmodname[racket/math]:
@fileblock["article.html.pm" @codeblock{
#lang pollen
◊(require racket/math)
Pi is close to ◊(number->string pi).
The hyperbolic sine of pi is close to ◊(number->string (sinh pi)).
π is close to ◊(number->string pi).
The hyperbolic sine of π is close to ◊(number->string (sinh pi)).
}]
The result:
@ -477,15 +497,15 @@ The result:
@repl-output{
'(root "Pi is close to " "3.141592653589793" "." "\n" "The hyperbolic sine of pi is close to " "11.548739357257748" ".")}
One caveat — you're still in a Pollen markup file, so the return value of whatever function you call has to produce a string or an X-expression, so it can be merged into the document. That's why we have @racket[number->string] wrapping the numerical values. @margin-note*{This is similar to the restriction introduced in the @seclink["Setting_up_a_preprocessor_source_file"]{first tutorial} where functions used in preprocessor files had to produce text.}
One caveat — you're still in a Pollen markup file, so the return value of whatever function you call has to produce a string or an X-expression, so it can be merged into the document. That's why we have @racket[number->string] wrapping the numerical values. (This is similar to the restriction introduced in the @seclink["Setting_up_a_preprocessor_source_file"]{first tutorial} where functions used in preprocessor files had to produce text.)
If your functions produce incompatible results, you'll get an error. For instance, look what happens when we remove @racket[number->string] from the example above.
@fileblock["article.html.pm" @codeblock{
#lang pollen
◊(require racket/math)
Pi is close to ◊|pi|.
The hyperbolic sine of pi is close to ◊(sinh pi).
π is close to ◊|pi|.
The hyperbolic sine of π is close to ◊(sinh pi).
}]
This will produce an error in DrRacket:
@ -493,17 +513,20 @@ This will produce an error in DrRacket:
@errorblock{
pollen markup error: in '(root "Pi is close to " 3.141592653589793 "." "\n" "The hyperbolic sine of pi is close to " 11.548739357257748 "."), 3.141592653589793 is not a valid element (must be txexpr, string, symbol, XML char, or cdata)}
This code would not, however, produce an error if it were being run as a Pollen preprocessor file, because the prepreocessor automatically converts numbers to strings. If you'd like to verify this, change the suffix to @code{.pp} and run the file again.
@subsection[#:tag-prefix "tutorial-3"]{Using the @filepath{pollen.rkt} file}
@subsection[#:tag-prefix "tutorial-3"]{Introducing @filepath{pollen.rkt}}
@(noskip-note)
As you get more comfortable attaching behavior to tags using tag functions, you'll likely want to create some functions that can be shared between multiple source files. The @filepath{pollen.rkt} file is a special file that is automatically imported by Pollen source files in the same directory (including within subdirectories). So every function and value provided by @filepath{pollen.rkt} can be used in these Pollen files.
As you get more comfortable attaching behavior to tags using tag functions, you'll likely want to create some functions that can be shared between multiple source files. The @filepath{pollen.rkt} file is a special file that is automatically imported by Pollen source files in the same directory (including subdirectories). So every function and value provided by @filepath{pollen.rkt} can be used in these Pollen files.
First, using @filepath{pollen.rkt} isn't mandatory. Within a Pollen source file, you can always import functions and values with @racket[require] (as seen in the previous section). @filepath{pollen.rkt} just makes it easier to propagate a set of common definitions to every every Pollen source file in your project.
First, using this file is not mandatory. You can always import functions and values from another file using @racket[require] (as seen in the previous section). The @filepath{pollen.rkt} is just meant to cure the tedium of importing the same file into every Pollen source file in your project. In a small project, not much tedium; in a large project, more.
Second, notice from the @filepath{.rkt} suffix that @filepath{pollen.rkt} is a source file containing Racket code, not Pollen code. This is the default because while Pollen's notation is more convenient for text-based source files, Racket's notation is more convenient when you're just dealing with code.
Second, notice from the @filepath{.rkt} suffix that @filepath{pollen.rkt} is a source file containing Racket code, not Pollen code. This is the default because while Pollen is better for text-driven source files, Racket is better for code-driven source files.
@margin-note{You can still use Pollen notation within a Racket source file. See @racketmodname[pollen/mode].}
Third, @filepath{pollen.rkt} always applies to Pollen source files in the same directory. But that's the minimum scope for the file, not the maximum. Pollen source files nested in subdirectories will look for a @filepath{pollen.rkt} in their own directory first. But if they can't find it, they'll look in the parent directory, then the next parent directory, and so on. Thus, by default, a @filepath{pollen.rkt} in the root folder of a project will apply to all the source files in the project. But when you add a new @filepath{pollen.rkt} to a subdirectory, it will apply to all files in that subdirectory and below.
@ -513,13 +536,13 @@ Let's see how this works in practice. In the same directory as @filepath{article
@fileblock["pollen.rkt" @codeblock{
#lang racket
(define author "Trevor Goodchild")
(provide author)
(define author "Trevor Goodchild")
}]
Here we use the @racket[define] function (which we've seen before) to set @racket[author] equal to @racket["Trevor Goodchild"]. Note the final step: consistent with standard Racket rules, we have to explicitly @racket[provide] the new value so that other files can see it (unlike Python, things you @racket[define] in Racket are private by default, not public).
Here we use the @racket[define] function (which we've seen before) to set @racket[author] equal to @racket["Trevor Goodchild"]. Note the final step: consistent with standard Racket rules, we have to explicitly @racket[provide] the new value so that other files can see it (unlike Python, things you @racket[define] in Racket are by default private, not public).
Then update good old @filepath{article.html.pm}:
Then update good old @filepath{article.html.pm} to use our new @racket[author] value:
@fileblock["article.html.pm" @codeblock{
#lang pollen
@ -531,7 +554,7 @@ Run this in DrRacket and you'll get:
@repl-output{'(root "The author is " "Trevor Goodchild" ".")}
Now, in the same dirctory, create a second Pollen source file:
Staying in the same dirctory, create a second Pollen source file:
@fileblock["barticle.html.pm" @codeblock{
#lang pollen
@ -543,18 +566,25 @@ Run this, and you'll get:
@repl-output{'(root "The author is really " "Trevor Goodchild" "?")}
That's all there is to it. Everything provided by @filepath{pollen.rkt} is automatically available within each Pollen source file.
That's all there is to it. You see how the value provided by @filepath{pollen.rkt} is automatically available within both Pollen source files.
You can include functions, including tag functions, the same way. For instance, add a function for @racket[em]:
You can import functions, including tag functions, the same way. For instance, add a function for @racket[em]:
@fileblock["pollen.rkt" @codeblock{
#lang racket
(define author "Trevor Goodchild")
(define (em . parts) `(extra (big ,@"@"parts)))
(require txexpr)
(provide author em)
(define author "Trevor Goodchild")
(define (em . elements)
(txexpr 'extra-big empty elements))
}]
Then use it in a source file:
We have a new bit of notation here. Notice that we defined our tag function as @racket[(em . elements)] rather than @racket[(em word)]. The use of a dot before the last input argument makes it into a @defterm{rest argument}. This puts all the remaining input arguments — however many there are — into one list. In general, this is the best practice for tag functions, because you don't usually know in advance how many elements will be passed to the function as input (for more about this, see @secref["the-text-body"]).
The @racket[txexpr] function is a utility from the @racket[txexpr] package (which is installed with Pollen). It builds a new X-expression from a tag, attribute list, and list of elements.
Then we use our new tag function in a source file:
@fileblock["article.html.pm" @codeblock{
#lang pollen
@ -564,31 +594,43 @@ The ◊em{author} is ◊em{◊|author|}.
With the expected results:
@repl-output{'(root "The " (extra (big "author")) " is " (extra (big "Trevor Goodchild")) ".")}
@repl-output{'(root "The " (extra-big "author") " is " (extra-big "Trevor Goodchild") ".")}
By the way, if you just want to @racket[provide] everything in @filepath{pollen.rkt}, you can use the @racket[all-defined-out] shorthand:
@fileblock["pollen.rkt" @codeblock{
#lang racket
(require txexpr)
(provide (all-defined-out)) ; provides `author` and `em`
(define author "Trevor Goodchild")
(define (em . elements)
(txexpr 'extra-big empty elements))
}]
@;subsection{Importing from a Pollen source file}
@;subsection{Making a Racket package}
@section{Decoding markup with a @tt{root} tag function}
@section{Decoding markup via the @tt{root} tag}
As you've seen, the X-expression you get when you run a Pollen markup file always starts with a tag called @code{root}. You can attach a custom tag function to @code{root} the same way as any other tag — by creating a new function and calling it @code{root}.
As you've seen, the X-expression you get when you run a Pollen markup file always starts with a node called @code{root}. You can attach a tag function to @code{root} the same way as any other tag. For instance, you could do something simple, like change the name of the output X-expression:
For instance, you could do something simple, like change the name of the output X-expression:
@fileblock["article.html.pm" @codeblock|{
#lang pollen
◊(define (root . elements) `(content ,@elements))
◊(require txexpr)
◊(define (root . elements)
(txexpr 'content empty elements))
The ◊code{root} tag is now called ◊code{content}.
}|]
Resulting in:
@repl-output{'(content "The " (tt "root") " tag is now called " (tt "content") ".")}
@repl-output{'(content "The " (code "root") " tag is now called " (code "content") ".")}
But unlike other tags in your document, @code{root} contains the entire content of the document. So the function you attach to @code{root} can operate on everything.
Unlike other tags in your document, @code{root} contains the entire content of the document. So the function you attach to @code{root} can operate on everything.
For that reason, one of the most useful things you can do with a tag function attached to @code{root} is @defterm{decoding} the content of the page. Decoding refers to any post-processing of content that happens after the tags within the page have been evaluated.
For that reason, one of the most useful things you can do with a tag function attached to @code{root} is @defterm{decoding} the content of the page. By decoding, I mean any post-processing of content that happens after the tags within the page have been evaluated.
Decoding is a good way to automatically accomplish:
@ -606,7 +648,7 @@ Decoding is a good way to automatically accomplish:
]
As an example, let's take one of my favorites — linebreak and paragraph detection. In XML authoring, you have to insert every @code{<br />} and @code{<p>} tag by hand. This is profoundly dull, clutters up the source file, and makes editing a chore.
As an example, let's take one of my favorites — linebreak and paragraph detection. In XML & HTML authoring, you have to insert every @code{<br />} and @code{<p>} tag by hand. This is profoundly dull, clutters the source file, and makes editing a chore.
Instead, let's make a decoder that allows us to denote a linebreak with a single newline in the source, and a paragraph break with a double newline. Here's some sample content with single and double newlines:
@ -623,9 +665,9 @@ Because we don't yet have a decoder, these newlines just get passed through:
@repl-output{'(root "The first line of the 'first' paragraph." "\n" "And a new line." "\n" "\n" "The second paragraph --- isn't it great.")}
When this X-expression is converted to HTML, the newlines persist:
When this X-expression is converted to HTML, the newlines will persist:
@repl-output{<root>The first line of the 'first' paragraph.\nAnd a new line.\n\nThe second paragraph --- isn't it great.</root>}
@terminal{<root>The first line of the 'first' paragraph.\nAnd a new line.\n\nThe second paragraph --- isn't it great.</root>}
But in HTML, raw newlines are displayed as a single space. So if you view this file in the project server, you'll see:
@ -636,18 +678,15 @@ The first line of the 'first' paragraph. And a new line. The second paragraph --
Not what we want.
So we need to make a decoder. To do this, we use the @racket[decode-elements] function, which provides hooks to selectively process certain categories of content within the document.
@margin-note{@racket[decode-elements] is a convenience variant of @racket[decode], which takes a full X-expression as input. Under the hood, they work the same way, so use whichever you prefer.}
So we need to make a decoder that will convert the newlines in our source into line breaks and paragraph breaks on the HTML output side. To do this, we use the @racket[decode-elements] function, which provides hooks to process categories of content within the document.
Add a basic @racket[decode-elements] to the source file like so:
@fileblock["article.html.pm" @codeblock|{
#lang pollen
◊(require pollen/decode pollen/tutorial txexpr)
◊(require pollen/decode txexpr)
◊(define (root . elements)
(make-txexpr 'root null (decode-elements elements)))
(txexpr 'root empty (decode-elements elements)))
The first line of the 'first' paragraph.
And a new line.
@ -655,20 +694,19 @@ And a new line.
The second paragraph --- isn't it great.
}|]
The @racket[make-txexpr] function is a utility from the @racket[txexpr] package, which is installed with Pollen. It builds a new X-expression from a tag, attribute list, and list of elements. Here, we'll keep the tag name @code{root}, leave the attributes as @code{null}, and append our decoded list of elements.
Here, we'll keep the tag name @code{root}, leave the attributes as @code{empty}, and pass through our decoded list of elements.
@margin-note{Racket jocks: you could also write this using @racket[quasiquote] and @racket[unquote-splicing] syntax as @code|{`(root ,@(decode-elements elements))}|. The @racket[txexpr] package is just an alternate way of accomplishing the task.}
If you run this file, what changes? Right — nothing. That's because by default, both @racket[decode-elements] (and @racket[decode]) will let the content pass through unaltered.
If you run this file, what changes? Right — nothing. That's because by default, @racket[decode-elements] will let the content pass through unaltered.
We change this by giving @racket[decode-elements] the name of a processing function and attaching it to the type of content we want to process. In this case, we're in luck — the @racket[decode] module already contains a @racket[decode-paragraphs] function (that also detects linebreaks). We add this function using the keyword argument @racket[#:txexpr-elements-proc], which is short for ``the function used to process the elements of a tagged X-expression'':
@fileblock["article.html.pm" @codeblock|{
#lang pollen
◊(require pollen/decode pollen/tutorial txexpr)
◊(require pollen/decode txexpr)
◊(define (root . elements)
(make-txexpr 'root null (decode-elements elements
(make-txexpr 'root empty (decode-elements elements
#:txexpr-elements-proc decode-paragraphs)))
The first line of the 'first' paragraph.
@ -681,9 +719,9 @@ Now, when we run the file, the X-expression has changed to include two @racket[p
@repl-output{'(root (p "The first line of the 'first' paragraph." (br) "And a new line.") (p "The second paragraph --- isn't it great."))}
That means when we convert to HTML, we get the tags we need:
That means when we convert to HTML, we'll get the tags we want:
@repl-output{<root><p>The first line of the 'first' paragraph.<br />And a new line.</p><p>The second paragraph --- isn't it great.</p></root>}
@terminal{<root><p>The first line of the 'first' paragraph.<br />And a new line.</p><p>The second paragraph --- isn't it great.</p></root>}
So when we view this in the project server, the linebreaks and paragraph breaks are displayed correctly:
@ -699,11 +737,11 @@ Of course, in practice you wouldn't put your decoding function in a single sourc
@fileblock["pollen.rkt" @codeblock{
#lang racket
(require pollen/decode pollen/tutorial txexpr)
(require pollen/decode txexpr)
(provide root)
(define (root . elements)
(make-txexpr 'root null (decode-elements elements
(make-txexpr 'root empty (decode-elements elements
#:txexpr-elements-proc decode-paragraphs)))
(provide root)
}]
We'll also restore the source of @filepath{article.html.pm} to its original, simplified state:
@ -717,7 +755,7 @@ And a new line.
The second paragraph --- isn't it great.
}|]
And the result in the project server will be the same:
This time, @filepath{article.html.pm} will pull in the tag function for @racket[root] from @filepath{pollen.rkt}. Otherwise, the code hasn't changed, so the result in the project server will be the same:
@browser{
The first line of the 'first' paragraph.
@ -728,21 +766,21 @@ The second paragraph --- isn't it great.
But wait, those straight quotes look terrible. Also, three hyphens for an em dash? Barbaric.
Let's upgrade our decoder to take of those. In the @racket[pollen/tutorial] module I've stashed the two functions we'll need for the job: @racket[smart-quotes] and @racket[smart-dashes].
Let's upgrade our decoder to take of those. In @racket[pollen/misc/tutorial] I've stashed the two functions we'll need for the job: @racket[smart-quotes] and @racket[smart-dashes].
This time, however, we're going to attach them to another part of @racket[decode-elements]. Smart-quote and smart-dash conversion only needs to look at the strings within the X-expression. So instead of attaching these functions to the @racket[#:txexpr-elements-proc] argument of @racket[decode-elements], we'll attach them to @racket[#:string-proc], which lets us specify a function to apply to strings:
@fileblock["pollen.rkt" @codeblock{
#lang racket/base
(require pollen/decode pollen/tutorial txexpr)
(require pollen/decode pollen/misc/tutorial txexpr)
(provide root)
(define (root . elements)
(make-txexpr 'root null (decode-elements elements
(make-txexpr 'root empty (decode-elements elements
#:txexpr-elements-proc decode-paragraphs
#:string-proc (compose smart-quotes smart-dashes))))
(provide root)
#:string-proc (compose1 smart-quotes smart-dashes))))
}]
Because @racket[#:string-proc] only accepts one function (not two), we need to use @racket[compose] to combine @racket[smart-quotes] and @racket[smart-dashes] into one (@racket[compose], from the Racket library, will apply the last function, then the previous one, and so on to the left end of the list).
Because @racket[#:string-proc] only accepts one function (not two), we need to use @racket[compose1] to combine @racket[smart-quotes] and @racket[smart-dashes] into one function (@racket[compose1], from the Racket library, creates a new function that applies each function in its argument list, from right to left).
Now, if we run @filepath{article.html.pm} in DrRacket, we can see the effects of the new decoder functions. The quotes are curled, and the three hyphens become an em dash:
@ -757,31 +795,31 @@ And a new line.
The second paragraph—isnt it great.
}
By the way, even though decoding via the @code{root} tag is the most likely use case, you don't have to do it that way. Decoding is just a special kind of tag function. So you can make a decoder that only affects a certain tag within the page. Or you can make multiple decoders for different tags. The advantage of using a decoder with @code{root} is that it can affect all the content, and since it's attached to the root node, it will always be the last tag function that gets called.
By the way, decoding via the @code{root} tag is often most convenient, but you don't have to do it that way. Decoding is just a special thing you can do inside any tag function. So you can make a decoder that only affects a certain tag on the page. Or you can make multiple decoders for different tags. The advantage of using a decoder with @code{root} is that it can affect all the content, and since it's attached to the root node, it will always be the last tag function that gets called.
@section{Putting it all together}
For this final example, we'll combine what we've learned in the first three tutorials. Though this project is still simple, it summarizes all the major concepts of Pollen.
It also provides a recipe you can adapt for your own projects, whether small or large. For instance, @italic{@link["http://practicaltypography.com"]{Butterick's Practical Typography}} follows this core structure.
It also provides a recipe you can adapt for your own projects, whether small or large. For instance, @italic{@link["http://practicaltypography.com"]{Butterick's Practical Typography}} and @italic{@link["http://typographyforlawyers.com"]{Typography for Lawyers}} follow this core structure.
As we go through the ingredients, I'll review the purpose of each. Save these files into a single project directory with the project server running.
@subsection[#:tag-prefix "tutorial-3"]{The @filepath{pollen.rkt} file}
This file provides functions that are available to all Pollen source files in the same directory. It's written in standard Racket. The @filepath{pollen.rkt} file is optional — without it, your tags will just be treated as default tag functions. But you'll probably find it a convenient way to make tag functions available within your project, including a @racket[decode] function attached to @code{root}.
This file provides functions that are automatically imported into Pollen source files in the same directory. It's written in standard Racket. The @filepath{pollen.rkt} file is optional — without it, your tags will just be treated as default tag functions. But you'll probably find it a convenient way to make tag functions available within your project, including a @racket[decode] function attached to @code{root}.
Here, we'll use the @filepath{pollen.rkt} we devised in the previous section to set up decoding for our source files:
@fileblock["pollen.rkt" @codeblock{
#lang racket/base
(require pollen/decode pollen/tutorial txexpr)
(require pollen/decode pollen/misc/tutorial txexpr)
(provide root)
(define (root . elements)
(make-txexpr 'root null (decode-elements elements
(make-txexpr 'root empty (decode-elements elements
#:txexpr-elements-proc decode-paragraphs
#:string-proc (compose smart-quotes smart-dashes))))
(provide root)
}]
@ -791,9 +829,9 @@ When you're using Pollen authoring mode for your content — using either Markdo
By default, when Pollen finds a source file called @filepath{filename.ext.pm} or @filepath{filename.ext.pmd}, it will look for a template in your project directory called @filepath{template.ext}, where @filepath{.ext} is the matching output extension.
In this project, we want to end up with HTML, so our source files will be called @filepath{filename.html.pm}, and thus we need to make a @filepath{template.html}. Let's use a modified version of the one we made in the second tutorial:
In this project, we want to end up with HTML, so our source files will be called @filepath{filename.html.pm}, and thus we need to make a @filepath{template.html}. Let's use a modified version of the one we made in the second tutorial. As we did then, let's add the null extension to clearly indicate it's an input file, so the whole name is @filepath{template.html.p}:
@fileblock["template.html"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
#lang pollen
<html>
@ -944,17 +982,17 @@ Now visit the project server and view @filepath{burial.html}, which should look
@image/rp["burial.png" #:scale 0.8]
Click the navigational links at the top to move between pages. You're encouraged to change the source files, the style sheet, the template, or @filepath{pollen.rkt}, and see how these changes immediately affect the page rendering in the project server. (You can also change the sequence of the pages in @filepath{index.ptree}, but in that case, you'll need to restart the project server to see the change.)
Click the navigational links at the top to move between pages. I encourage you to change the source files, the style sheet, the template, or @filepath{pollen.rkt}, and see how these changes immediately affect the page rendering in the project server. (You can also change the sequence of the pages in @filepath{index.ptree}, but in that case, you'll need to restart the project server to see the change.)
This page isn't a miracle of web design, but it shows you in one example:
This page isn't a miracle of web design. But it shows you in one example:
@itemlist[
@item{Pollen markup being decoded — paragraph breaks, linebreaks, smart quotes, smart dashes — with a @racket[decode] function attached to the @code{root} node by @filepath{pollen.rkt};}
@item{Pollen markup being decoded — paragraph breaks, linebreaks, smart quotes, smart dashes — with a @racket[decode] function attached to the @code{root} node by @filepath{pollen.rkt}.}
@item{A dynamically-generated CSS file that computes positions for CSS elements using numerical values set up with @racket[define], and mathematical conversions thereof;}
@item{A CSS file generated by the Pollen preprocessor that computes positions for CSS elements using numerical values set up with @racket[define], and mathematical conversions thereof.}
@item{Navigational links that appear and disappear as needed using conditional statements (@racket[when/splice]) in @filepath{template.html}, with the page sequence defined by @filepath{index.ptree} and the names of the links being pulled from the @code{h1} tag of each source file using @racket[select].}
@item{Navigational links that appear and disappear as needed using conditional statements (@racket[when/splice]) in @filepath{template.html.p}, with the page sequence defined by @filepath{index.ptree} and the names of the links being pulled from the @code{h1} tag of each source file using @racket[select].}
]

@ -8,7 +8,7 @@
Consistent with Racket's @seclink["Package_Concepts" #:doc '(lib "pkg/scribblings/pkg.scrbl")]{version-numbering system}, the first digit reflects major updates to Pollen that break backward compatibility. The second digit reflects feature updates that don't affect existing features.
Inconsistent with this system, Pollen's version also appends a build number, which is the age of the software in days and seconds. (The official version reported in Pollen's @filepath{info.rkt} is just the major + minor digits.)
Inconsistent with this system, Pollen's version also appends a build number, which is the age of the software in days and minutes. (The official version reported in Pollen's @filepath{info.rkt} is just the major + minor digits.)
@section{Source code}

@ -19,4 +19,4 @@
(define-meta zing "bam")
(select 'zing metas))
(require (prefix-in markdown: 'markdown))
(check-equal? markdown:doc '(root (p () "bam")))
(check-equal? markdown:doc '(root (p "bam")))

@ -14,4 +14,4 @@
(module markdown pollen/markdown
"Hello" (when #t (@ "Splice")) "" (when/splice #t "Splice") "World")
(require (prefix-in markdown: 'markdown))
(check-equal? markdown:doc '(root (p () "HelloSpliceSpliceWorld")))
(check-equal? markdown:doc '(root (p "HelloSpliceSpliceWorld")))

@ -22,7 +22,7 @@
(module test-markdown pollen/markdown
"hello world")
(require (prefix-in markdown: 'test-markdown))
(check-equal? markdown:doc '(root (p () "hello world")))
(check-equal? markdown:doc '(root (p "hello world")))
(module test-ptree pollen/ptree

@ -10,7 +10,8 @@
rackjure/str
xml
(only-in html read-html-as-xml)
"../private/debug.rkt")
"../private/debug.rkt"
"../private/splice.rkt")
(provide highlight make-highlight-css)
@ -151,7 +152,7 @@ if zero is False:
(define (highlight lang . codelines)
(define code (string-append* codelines))
`(div ((class "highlight"))
,@(pygmentize code lang)))
,@(strip-empty-attrs (pygmentize code lang))))
;; Other CSS options available from http://richleland.github.io/pygments-css/

Loading…
Cancel
Save