The cache is supposed to take note of the `POLLEN` environment variable, but due to the spurious `string-downcase` here, was instead looking at `pollen`, which is a different key on case-sensitive systems. Windows environment variables are not case-sensitive, but it seems Racket’s `getenv` function will handle the case conversion as needed.
When recursive mode is used, `current-project-root` changes during the extent of a render operation. But if parallel rendering is also used, when the main thread re-parameterizes `current-project-root`, it will have no effect on the parallel threads, because they freshly instantiate `current-project-root` (with its default value). This patch moves the parameterization inside the parallel thread by passing the `current-project-root` value as part of the job message, thereby handling it the same way as `current-poly-target`.
* Specify external renderer via module and id
* faster external render check
* Update render.rkt
* rely on default exception messages, which are informative
Pagetree promises that its nodes will be resolved relative to the directory where the pagetree lives. For path-based pagetrees, make sure this directory is set correctly.
Clarify three things:
1. You can use pagetree files with `raco pollen render`
2. `--target` doesn’t prevent other file types from being rendered
3. The generated pagetree includes more than just source files
* ci: run CI for Racket 6.6 and up
Racket 6.5 has an SSL issue that makes it unable to install packages.
That might be why it was not included in the Travis build either.
* ci: drop travis config
* start command: fix default value for port
Defaulting the port to "8080" at the `command-line' level breaks
customization via `pollen.rkt'.
* test-project-port: connect to server to verify that it's up
Removes many (not all) pdflatex errors when generating PDF docs. Some
styles had to be renamed because they get reused as LaTeX
environments/commands which can only contain letters.
I welcome pull requests. But accepting a PR obligates me to maintain that code for the life of Pollen. So if I seem picky about which PRs I accept —yes, because I have to be. No hard feelings. (= Principle of Infinite Maintenance)
* There’s plenty of room for improvement in the Pollen code, because every line of it has been written against the backdrop of ignorance and fallibility, mostly my own. (= Principle of Prior Ignorance)
* PRs for simple documentation fixes (e.g., spelling and grammar corrections) are always welcome. For more substantial changes, I don’t necessarily prefer PRs to issues or feature requests. A good description of the problem with a working example is better than a half-baked PR. I can often fix it in less time than it would take to review the PR. (= Principle of Efficiency)
* If you want feedback on a potential PR, I recommend posting to the [Pollen forum](https://forums.matthewbutterick.com/c/typesetting/) rather than here. Because more people will see it. (= Principle of Exposure)
* Small PRs are easier to accept than large ones. Large PRs should have a benefit worthy of their complexity. PRs that want to amend Pollen’s public interface receive the highest scrutiny. (= Principle of Proportionality)
* I consider every PR, but I can’t promise detailed code reviews or comments. Helpful Racketeers can be found on the [Pollen forum](https://forums.matthewbutterick.com/c/pollen/), the [Racket mailing list](https://lists.racket-lang.org/), and the Racket [Slack channel](https://racket.slack.com/). (= Principle of Specialization)
* PRs should be necessary, in the sense that the proposed change can only be accomplished by patching this repo. (Corollary: features that can live in a separate [package](https://pkgs.racket-lang.org/) probably should.) (= Principle of Necessity)
* PRs should avoid introducing magic behavior (= [Principle of Least Astonishment](http://wiki.c2.com/?PrincipleOfLeastAstonishment)).
* PRs should forbid as little as possible. In particular, PRs should avoid enshrining personal preference as default behavior (because others will have different preferences). (= Principle of Generality)
* PRs should avoid reinventing features that already exist in Racket. (= Principle of Economy)
* PRs should fix real problems that have arisen in actual use, not theoretical or conjectural problems. (= Principle of Practical Justification)
* I follow these principles too, because they’re virtuous habits. Still, I created Pollen as a tool for my writing and typography work. If a certain PR would negatively impact that work, I can’t accept it. (= Principle of Royalty)
* If you’re new to Pollen or Racket, your PRis more likely to be declined, because certain things you perceive as bugs are actually features, certain things you perceive as missing are actually present, and certain limitations you perceive as surmountable are actually not. (Seealso point #1 re: backdrop of ignorance.) (= Principle of Novelty)
* If your PR includes open-source material from elsewhere, please make sure that material is a) compatible with the Pollen license and b) attributed in whatever way is required. Otherwise, I cannot accept it. (= Principle of Legality)
* PRs that could have unit tests, and don’t, will be treated harshly. As they should. (= Principle of Proof)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## Pollen: the book is a program [![Build Status](https://travis-ci.org/mbutterick/pollen.svg?branch=master)](https://travis-ci.org/mbutterick/pollen)
## Pollen: the book is a program [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](CODE_OF_CONDUCT.md)
A book-publishing system written in [Racket](http://racket-lang.org). This is the software I use to publish & maintain my web-based books [Beautiful Racket](http://beautifulracket.com), [Practical Typography](http://practicaltypography.com), and [Typography for Lawyers](http://typographyforlawyers.com).
A book-publishing system written in [Racket](http://racket-lang.org). This is the software I use to publish & maintain my web-based books [Beautiful Racket](http://beautifulracket.com), [Practical Typography](http://practicaltypography.com), and [Typography for Lawyers](http://typographyforlawyers.com).
@ -15,7 +15,7 @@ Pollen gives you access to a full programming language (Racket) with a text-base
Official mailing list: http://groups.google.com/forum/#!forum/pollenpub
Official forum: https://forums.matthewbutterick.com/c/typesetting/
## License
## License
LGPL
MIT
## Pull-request tips
## Project status
I welcome pull requests. But accepting a PR obligates me to maintain that code for the life of Pollen. So if I seem picky about which PRs I accept —yes, because I have to be. No hard feelings.
Actively developed, though the pace has slowed now that Pollen is arguably feature complete and stable. I use it almost every day so it's not going anywhere. But I have no plans to substantially enlarge or extend it.
* There’s plenty of room for improvement in the Pollen code, because every line of it has been written against the backdrop of ignorance and fallibility, mostly my own.
* I don’t necessarily prefer PRs to issues or feature requests. A good description of the problem with a working example is better than a half-baked PR. I can often fix it in less time than it would take to review the PR.
* If you want feedback on a potential PR, I recommend posting to the [Pollen mailing list](http://groups.google.com/forum/#!forum/pollenpub) rather than here. Because more people will see it.
* Small PRs are easier to accept than large ones. Large PRs should have a benefit worthy of their complexity.
* I consider every PR, but I can’t promise detailed code reviews or comments. Helpful Racketeers can be found on the [Pollen mailing list](http://groups.google.com/forum/#!forum/pollenpub), the [Racket mailing list](https://lists.racket-lang.org/), and the Racket [Slack channel](https://racket.slack.com/).
* PRs should be necessary, in the sense that the proposed change can only be accomplished by patching this repo. (Corollary: features that can live in a separate [package](https://pkgs.racket-lang.org/) probably should.)
* PRs should avoid introducing magic behavior (aka the [principle of least astonishment](http://wiki.c2.com/?PrincipleOfLeastAstonishment)).
* PRs should forbid as little as possible. In particular, PRs should avoid enshrining personal preference as default behavior (because others will have different preferences).
* PRs should avoid reinventing features that already exist in Racket.
* I follow these principles too, because they’re virtuous habits. Still, I created Pollen as a tool for my writing and typography work. If a certain PR would negatively impact that work, I can’t accept it.
* If you’re new to Pollen or Racket, your PRis more likely to be declined, because certain things you perceive as bugs are actually features, certain things you perceive as missing are actually present, and certain limitations you perceive as surmountable are actually not. (Seealso point #1 re: backdrop of ignorance.)
* PRs that could have unit tests, and don’t, will be treated harshly. As they should.
* PRs that want to amend Pollen’s public interface receive the highest scrutiny.
#:argv(vector-drop(current-command-line-arguments)1); snip the 'setup' from the front
#:once-any
[("-p""--parallel")"Setup in parallel using all cores"(setup-parallel?#true)]
[("-j""--jobs")job-count"Setup in parallel using <job-count> jobs"(setup-parallel?(or(string->numberjob-count)(raise-argument-error'handle-setup"exact positive integer"job-count)))]
[("-d""--dry-run")"Print paths that would be compiled"(dry-run?#true)]
[("-p""--parallel")"Render in parallel using all cores"(render-parallel?#true)]
[("-j""--jobs")job-count"Render in parallel using <job-count> jobs"(render-parallel?(or(string->numberjob-count)(raise-argument-error'handle-render"exact positive integer"job-count)))]
#:argsother-args
#:argsother-args
other-args))
other-args))
(definetimestamp(current-seconds)); keeps timestamp consistent through whole render
(define(handle-batch-renderpaths)
(when(force-render?)
;; forcing works like `touch`: updates the mod date of the files,
@ -15,7 +15,7 @@ This is the core design principle of Pollen. Consistent with this principle, Pol
@item{@bold{A Pollen project consists of source files + static files.} A @italic{source file} is a file that can be compiled to produce certain output. A @italic{static file} is usable as it stands (e.g., an SVG file or webfont). Generally, the textual content of your book will live in source files, and other elements will be static files.}
@item{@bold{A Pollen project consists of source files + static files.} A @italic{source file} is a file that can be compiled to produce certain output. A @italic{static file} is usable as it stands (e.g., an SVG file or webfont). Generally, the textual content of your book will live in source files, and other elements will be static files.}
@item{@bold{Source control is a good idea.} Because Pollen projects are software projects, they can be easily managed with systems for source control and collaboration, like @link["http://github.com"]{GitHub}. If you're a writer at heart, don't fear these systems —the learning curve is repaid by revision & edit tracking that's much easier than it is with Word or PDF files.}
@item{@bold{Source control is a good idea.} Because Pollen projects are software projects, they can be easily managed with systems for source control and collaboration. If you're a writer at heart, don't fear these systems —the learning curve is repaid by revision & edit tracking that's much easier than it is with Word or PDF files.}
The slowest part of a Pollen @racket[render] is compiling a source file. Because Pollen allows source files to be edited and previewed dynamically, these files get recompiled a lot. Therefore, Pollen stores copies of the exports of source files —namely, whatever is stored in @code[(format "~a" default-main-export)] and @code[(format "~a" default-meta-export)] —in a cache so they can be reused.
The slowest part of a Pollen @racket[render] is compiling a source file. Because Pollen allows source files to be edited and previewed dynamically, these files get recompiled a lot. Therefore, Pollen stores copies of the exports of source files —namely, whatever is stored in @code[(format "~a" pollen-main-export)] and @code[(format "~a" pollen-meta-export)] —in a cache so they can be reused.
In each directory of your project, Pollen writes cache files into a subdirectory called @filepath{compiled}. The files are stored on disk so they can be reused between sessions. If you delete files within a cache directory (or the whole thing), don't worry —everything will get regenerated. (However, I don't recommend trying to read or write directly to any @filepath{compiled} directory, as the implementation details of the cache are subject to change.)
In each directory of your project, Pollen writes cache files into a subdirectory called @filepath{compiled}. The files are stored on disk so they can be reused between sessions. If you delete files within a cache directory (or the whole thing), don't worry —everything will get regenerated. (However, I don't recommend trying to read or write directly to any @filepath{compiled} directory, as the implementation details of the cache are subject to change.)
@ -43,13 +43,13 @@ Be warned that this will make your rendering much slower. But you will be guaran
@section{Scope of dependency tracking}
@section{Scope of dependency tracking}
The compile cache tracks the modification date of the source file, the current setting of @secref["The_POLLEN_environment_variable"], and the modification dates of the template and @filepath{pollen.rkt} (if they exist).
The compile cache tracks the modification date of the source file, the current setting of @secref["The_POLLEN_environment_variable"], and the modification dates of the template and @filepath{pollen.rkt} (if they exist). For @tt{poly} source files, it also tracks the @racket[current-poly-target]. It also tracks files you've listed in the optional setup value @racket[setup:cache-watchlist] and environment variables listed in the optional setup value @racket[setup:envvar-watchlist].
It does not, however, track every possible dependency. So in a complex project, it's possible to create deep dependencies that aren't noticed by the cache.
It does not, however, track every possible dependency. So in a complex project, it's possible to create deep dependencies that aren't noticed by the cache. In particular, Pollen does not track pagetree files as dependencies of other source files. Thus, if you change a pagetree, you'll ordinarily need to use @exec{raco pollen reset} to clear the caches.
Unfortunately, there's no way around this problem. For the cache to be useful, there has to be a limit on the horizon of dependency checking. To capture every possible dependency, the cache would have to recompile every file, every time —which would be equivalent to not caching at all.
Unfortunately, there's no way around this problem. For the cache to be useful, there has to be a limit on the horizon of dependency checking. To capture every possible dependency, the cache would have to recompile every file, every time —which would be equivalent to not caching at all.
Those who need that kind of deep dynamism can disable the cache.
Those who need that kind of deep dynamism can disable the cache (with the setup values @racket[setup:render-cache-active] and @racket[setup:compile-cache-active]).
@section[#:tag-prefix "cache"]{Functions}
@section[#:tag-prefix "cache"]{Functions}
@ -70,8 +70,6 @@ Attempt to retrieve the requested value out of the cache. If it's not there, or
These functions are the lower-level cousins of @racket[get-doc] and @racket[get-metas], which have a more convenient interface. Unless you have a special reason, you're better off using those.
These functions are the lower-level cousins of @racket[get-doc] and @racket[get-metas], which have a more convenient interface. Unless you have a special reason, you're better off using those.
Despite their names, these functions actually rely on @racket[setup:main-export] and @racket[setup:meta-export] (which default to @id[default-main-export] and @id[default-meta-export]). Thus, if you override those names, everything will still work as expected.
If you want the speed benefit of the cache, you should use @racket[cached-doc] and @racket[cached-metas] to get data from Pollen source files in preference to functions like @racket[require], @racket[local-require], and @racket[dynamic-require]. Those will also work. They'll just be slower.
If you want the speed benefit of the cache, you should use @racket[cached-doc] and @racket[cached-metas] to get data from Pollen source files in preference to functions like @racket[require], @racket[local-require], and @racket[dynamic-require]. Those will also work. They'll just be slower.
@ -21,6 +21,8 @@ Pollen uses a special character — the @italic{lozenge}, which looks like this:
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.
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.
Consideration (b) is especially important in a text-based language like Pollen. If Pollen used something more common as its command character, then every time you used that character in text, you'd have to specially escape it. This would make it cumbersome and annoying to import plain text into Pollen source files. This is the Pareto-optimal trade.
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.
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.
If you're using a different editor, here's how you type it:
If you're using a different editor, here's how you type it:
@ -29,9 +31,9 @@ If you're using a different editor, here's how you type it:
@(linebreak)@bold{Windows}: holding down Alt, type 9674 on the num pad
@(linebreak)@bold{Windows}: holding down Alt, type 9674 on the num pad
@(linebreak)@bold{GNU/Linux, BSD}: Type Ctrl + Shift + U, then 25CA, then Enter
@(linebreak)@bold{GNU/Linux, BSD}: Type Ctrl + Shift + U, then 25CA, then Enter
For more information on entering arbitrary Unicode glyphs, see @link["https://en.wikipedia.org/wiki/Unicode_input"]{Wikipedia}.
For more information on entering arbitrary Unicode characters, see @link["https://en.wikipedia.org/wiki/Unicode_input"]{Wikipedia}.
@subsection{``But I don't want to use it ...''}
@subsection{``But I don't want to use the lozenge ...''}
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"].
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"].
@ -43,7 +45,7 @@ My advice: don't knock the lozenge till you try it.
@subsubsection{How MB types the lozenge}
@subsubsection{How MB types the lozenge}
I work on Mac OS. I use @link["http://www.ergonis.com/products/typinator/"]{Typinator} to assign the @literal{◊} character to the otherwise never-used Option + Shift + backlash key combo (ordinarily assigned to @literal{»}). For that matter, I assign @literal{λ} to Option + backslash (ordinarily assigned to @literal{«}).
I work on Mac OS. I use @link["http://www.ergonis.com/products/typinator/"]{Typinator} to assign the @literal{◊} character to the otherwise never-used Option + Shift + backslash key combo (ordinarily assigned to @literal{»}). For that matter, I assign @literal{λ} to Option + backslash (ordinarily assigned to @literal{«}).
@subsubsection{DrRacket toolbar button}
@subsubsection{DrRacket toolbar button}
@ -612,7 +614,7 @@ Second, the metas are collected into a hash table that is exported with the name
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.)
The only key that's automatically defined in every meta table is @racket['#,pollen-here-path-key], 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:
Still, you can override this too:
@ -661,6 +663,32 @@ The title is ◊(select-from-metas 'title metas)
@repl-output{'(root "The title is " "Conclusion to " (em "Infinity War"))}
@repl-output{'(root "The title is " "Conclusion to " (em "Infinity War"))}
To save a few keystrokes, you can consolidate multiple key–value pairs into one @racket[define-meta] form. So this:
@codeblock{
#lang pollen
◊(define-meta dog "Roxy")
◊(define-meta cat "Chopper")
◊(define-meta ape "Koko")
}
Is the same as this:
@codeblock{
#lang pollen
◊(define-meta dog "Roxy"
cat "Chopper"
ape "Koko")
}
In both cases, the resulting metas look like this:
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}.
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}.
@ -720,12 +748,14 @@ And the metas:
@codeblock{
@codeblock{
#lang racket/base
#lang racket/base
(require "pollen-source.rkt") ; doc and metas and everything else
(require "path/to/your-pollen-source") ; doc and metas and everything else
(require (submod "pollen-source.rkt" metas)) ; just metas
(require (submod "path/to/your-pollen-source" 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.
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.
@bold{Pro tip #3}: Within a tag function, you can access the metas of the source currently being evaluated with @racket[current-metas].
@ -16,18 +16,22 @@ These functions are automatically imported into every Pollen source file (meanin
@section{Metas}
@section{Metas}
The only key that's automatically defined in every meta table is @racket['#,pollen-here-path-key], which holds the absolute path to the source file. For instance, you could retrieve this value with @racket[(select-from-metas '#,pollen-here-path-key metas)].
For a full introduction to metas, see @secref["Inserting_metas"].
@defform[(define-meta name value)]
@defform[(define-meta name value)]
Add @racket[_value] to the metas of the current document, using @racket[_name] as the key.
Add @racket[_value] to the metas of the current document, using @racket[_name] as the key.
You can retrieve a meta value —even in the same document where you define it — with @racket[(select-from-metas _name metas)].
You can retrieve a meta value —even in the same document where you define it — with @racket[(select-from-metas _name metas)].
For an introduction to metas, see @secref["Inserting_metas"].
@section{Splicing}
@section{Splicing}
@defform[(\@ arg ...)]
@defform[(\@ arg ...)]
Splicing tag: signals that a list should be merged into its containing expression. You can use something other than @racket[\@] by overriding @racket[setup:splicing-tag].
The splicing tag signals that a list should be merged into its containing expression. The splicing tag is @racket['\@].
@examples[#:eval my-eval
@examples[#:eval my-eval
(module splicer pollen/markup
(module splicer pollen/markup
@ -36,6 +40,24 @@ Splicing tag: signals that a list should be merged into its containing expressio
doc
doc
]
]
The splicing tag is useful when you want to return a list of X-expressions in a situation where you can only return one. For instance, @secref["Tag_functions"] can only return one X-expression. But if we wrap the list of X-expressions in a splicing tag, they behave like a single X-expression. Later, Pollen will merge the list elements into the surrounding expression (as shown above).
@examples[#:eval my-eval
(require pollen/tag)
(code:comment @#,t{wrong: function returns a list of X-expressions})
(define-tag-function (multi attrs elems)
'("foo" "bar"))
(code:comment @#,t{right: function returns a list of X-expressions})
(code:comment @#,t{as elements inside a splicing tag})
(define-tag-function (multi2 attrs elems)
'(\@ "foo" "bar"))
]
Though the splicing tag is cosmetically identical to the abbreviated notation of @litchar{@"@"} for @racket[unquote-splicing], and has a similar purpose, it's not the same thing. The splicing tag isn't a variable —it's just a symbol that Pollen treats specially when generating output.
@defform[(when/splice condition pollen-args)]
@defform[(when/splice condition pollen-args)]
If @racket[_condition] is true, put the @racket[_pollen-args] into the document. Within a template file, usually invoked like so:
If @racket[_condition] is true, put the @racket[_pollen-args] into the document. Within a template file, usually invoked like so:
@ -52,7 +74,7 @@ The inserted text can contain its own nested Pollen commands.
Like @racket[for/list] and @racket[for*/list], but the resulting list is spliced into the document.
Like @racket[for/list] and @racket[for*/list], but the resulting list is spliced into the document.
@pollen-history[#:added "1.4"]
@history[#:added "1.4"]
@section{Data helpers}
@section{Data helpers}
@ -145,7 +167,7 @@ Note that if @racket[_doc-source] is a relative path or pagenode, it is treated
(select-from-metas
(select-from-metas
[key symbolish?]
[key symbolish?]
[meta-source (or/c hash? pagenodeish? pathish?)])
[meta-source (or/c hash? pagenodeish? pathish?)])
(or/c #f xexpr?)]
any/c]
Look up the value of @racket[_key] in @racket[_meta-source]. The @racket[_meta-source] argument can be either 1)a hashtable representing @racket[metas] or 2)a pagenode or source path that identifies a source file that provides @racket[metas]. If no value exists for @racket[_key], you get @racket[#f].
Look up the value of @racket[_key] in @racket[_meta-source]. The @racket[_meta-source] argument can be either 1)a hashtable representing @racket[metas] or 2)a pagenode or source path that identifies a source file that provides @racket[metas]. If no value exists for @racket[_key], you get @racket[#f].
Note that if @racket[_meta-source] is a relative path or pagenode, it is treated as being relative to @racket[current-project-root]. If that's not what you want, you'll need to convert it explicitly to a complete-path (e.g., with @racket[path->complete-path] or @racket[->complete-path]).
Note that if @racket[_meta-source] is a relative path or pagenode, it is treated as being relative to @racket[current-project-root]. If that's not what you want, you'll need to convert it explicitly to a complete-path (e.g., with @racket[path->complete-path] or @racket[->complete-path]).
@ -159,11 +181,11 @@ Note that if @racket[_meta-source] is a relative path or pagenode, it is treated
@defparam[current-metas val (or/c #f hash?) #:value #f]
Holds the @racket[metas] of the current Pollen source. In tag functions, rather than pass @racket[metas] as an argument, you can refer to @racket[(current-metas)] within the body of the function. Likewise, if your tag function calls other tag functions, they can all invoke @racket[(current-metas)] instead of passing the value around.
Holds the @racket[metas] of the current Pollen source. In tag functions, rather than pass @racket[metas] as an argument, you can refer to @racket[(current-metas)] within the body of the function. Likewise, if your tag function calls other tag functions, they can all invoke @racket[(current-metas)] instead of passing the value around.
@racket[(current-metas)] will also work in templates, holding the @racket[metas] of the source currently being rendered into the template.
@racket[(current-metas)] will also work in templates, holding the @racket[metas] of the source currently being rendered into the template.
The default value is @racket[#f]. This means that no metas value is available. It's your responsibility to handle this circumstance sensibly.
The default value is @racket[#f]. This means that no metas value is available. It's your responsibility to handle this circumstance sensibly.
@ -44,18 +44,7 @@ This function doesn't do much on its own. Rather, it provides the hooks upon whi
Recall that in Pollen, all @secref["tags-are-functions"]. By default, the @racket[_tagged-xexpr] from a source file is tagged with @racket[root]. So the typical way to use @racket[decode] is to attach your decoding functions to it, and then define @racket[root] to invoke your @racket[decode] function. Then it will be automatically applied to every @racket[doc] during compile.
Recall that in Pollen, all @secref["tags-are-functions"]. By default, the @racket[_tagged-xexpr] from a source file is tagged with @racket[root]. So the typical way to use @racket[decode] is to attach your decoding functions to it, and then define @racket[root] to invoke your @racket[decode] function. Then it will be automatically applied to every @racket[doc] during compile.
For instance, here's how @racket[decode] is attached to @racket[root] in @link["http://practicaltypography.com"]{@italic{Butterick's Practical Typography}}. There's not much to it —
@margin-note{@link["https://docs.racket-lang.org/pollen-tfl/_pollen_rkt_.html#%28def._%28%28lib._pollen-tfl%2Fpollen..rkt%29._root%29%29"]{Here's an example} of invoking @racket[decode] via the @racket[root] tag. That example is part of the @racket[pollen-tfl] sample project, which you can install & study separately.}
@margin-note{The @racket[hyphenate] function is not part of Pollen, but rather the @link["http://github.com/mbutterick/hyphenate"]{@racket[hyphenate] package}, which you can install separately.}
This illustrates another important point: even though @racket[decode] presents an imposing list of arguments, you're unlikely to use all of them at once. These represent possibilities, not requirements. For instance, let's see what happens when @racket[decode] is invoked without any of its optional arguments.
This illustrates another important point: even though @racket[decode] presents an imposing list of arguments, you're unlikely to use all of them at once. These represent possibilities, not requirements. For instance, let's see what happens when @racket[decode] is invoked without any of its optional arguments.
@ -302,9 +291,9 @@ The @racket[_linebreaker] argument can either be @racket[#f] (which will delete
Find paragraphs within @racket[_elements] and wrap them with @racket[_paragraph-wrapper]. Also handle linebreaks using @racket[decode-linebreaks].
Find paragraphs within @racket[_elements] and wrap them with @racket[_paragraph-wrapper]. Also handle linebreaks using @racket[decode-linebreaks].
What counts as a paragraph? Any @racket[_elements] that are either a) explicitly set apart with a paragraph separator, or b) adjacent to a @racket[block-txexpr?] (in which case the paragraph-ness is implied).
What counts as a paragraph? Any @racket[_elements] that are either a) explicitly set apart with a paragraph separator, or b) adjacent to a @racket[block-txexpr?] (in which case the paragraph-ness is implied).
@ -15,17 +15,17 @@ Pollen handles six kinds of source files:
@itemlist[
@itemlist[
@item{@bold{Preprocessor}, with file extension @ext[default-preproc-source-ext]}
@item{@bold{Preprocessor}, with file extension @ext[pollen-preproc-source-ext]}
@item{@bold{Markup}, with file extension @ext[default-markup-source-ext]}
@item{@bold{Markup}, with file extension @ext[pollen-markup-source-ext]}
@item{@bold{Markdown}, with file extension @ext[default-markdown-source-ext]}
@item{@bold{Markdown}, with file extension @ext[pollen-markdown-source-ext]}
@item{@bold{Null}, with file extension @ext[default-null-source-ext]}
@item{@bold{Null}, with file extension @ext[pollen-null-source-ext]}
@item{@bold{Scribble}, with file extension @ext[default-scribble-source-ext]}
@item{@bold{Scribble}, with file extension @ext[pollen-scribble-source-ext]}
@item{@bold{Pagetree}, with file extension @ext[default-pagetree-source-ext]. This is the only source type that does not produce an output file.}
@item{@bold{Pagetree}, with file extension @ext[pollen-pagetree-source-ext]. This is the only source type that does not produce an output file.}
]
]
@ -170,7 +170,7 @@ In all cases, if there is no corresponding source, return @racket[#f].
path?]
path?]
Convert a source path @racket[_p] into its corresponding output path. This function simply generates a path for a file — it does not ask whether the file exists.
Convert a source path @racket[_p] into its corresponding output path. This function simply generates a path for a file — it does not ask whether the file exists.
If @racket[_p] has a @seclink["The_poly_output_type"]{@id[default-poly-source-ext] output type}, then @racket[->output-path] uses @racket[current-poly-target] as the output-path extension.
If @racket[_p] has a @seclink["The_poly_output_type"]{@id[pollen-poly-source-ext] output type}, then @racket[->output-path] uses @racket[current-poly-target] as the output-path extension.
Otherwise, there are no type-specific variants for this function because the output path of a Pollen source file is @seclink["Saving___naming_your_source_file"]{determined by its name}.
Otherwise, there are no type-specific variants for this function because the output path of a Pollen source file is @seclink["Saving___naming_your_source_file"]{determined by its name}.
@ -27,7 +27,7 @@ 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.
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].
You can change the command character for a project by overriding @racket[pollen-command-char].
@bold{How is this different from Racket?} In Racket, everything is a command, and plain text must be quoted.
@bold{How is this different from Racket?} In Racket, everything is a command, and plain text must be quoted.
@ -42,17 +42,15 @@ There are no undefined commands in Pollen. If a command has not already been def
By default, every Pollen source file exports two identifiers:
By default, every Pollen source file exports two identifiers:
@itemlist[
@defthing[doc xexpr?]{
@item{@id{doc} contains the output of the file. The type of output depends on the source format (about which, more below).}
Contains the output of the file. The type of output depends on the source format (about which, more below).}
@item{@id{metas} is a hashtable of key–value 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).}
@defthing[metas hasheq?]{
]
A table of key–value pairs with extra information that is extracted from the source. These @racket[metas] will always contain the key @racket['#,pollen-here-path-key], 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).}
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[get-doc] and @racket[get-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[get-doc] and @racket[get-metas].
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.
Pollen source files also make the @racket[metas] hashtable available through a submodule, unsurprisingly called @racket[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 @@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 exported identifiers, but otherwise doesn't care how they're generated. Thus, the code inside your Pollen source file could be written in @tt{#langracket} or @tt{#langwhatever}. As long as you @racket[provide] those two identifiers and follow Pollen's file-naming conventions, your source file will be renderable.}
@margin-note{The Pollen rendering system relies on these two exported identifiers, but otherwise doesn't care how they're generated. Thus, the code inside your Pollen source file could be written in @tt{#langracket} or @tt{#langwhatever}. As long as you @racket[provide] those two identifiers and follow Pollen's file-naming conventions, your source file will be renderable.}
@ -71,9 +69,9 @@ If a file called @filepath{pollen.rkt} exists in the same directory with a sourc
@bold{How is this different from Racket?} In Racket, you must explicitly import files using @racket[require].
@bold{How is this different from Racket?} In Racket, you must explicitly import files using @racket[require].
Invoke the preprocessor dialect by using @code{#lang pollen/pre} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" default-preproc-source-ext)}. These forms are equivalent:
Invoke the preprocessor dialect by using @code{#lang pollen/pre} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" pollen-preproc-source-ext)}. These forms are equivalent:
@racketmod[#:file "sample.css.pp" pollen
@racketmod[#:file "sample.css.pp" pollen
@ -92,13 +90,13 @@ _...source...
Of course, you're better off specifying the preprocessor dialect explicitly rather than relying on this default behavior.
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 @id{doc}, is plain text. For this reason, the preprocessor will convert everything it finds to text in the least surprising way possible.
The output of the preprocessor dialect, provided by @racket[doc], is plain text. For this reason, the preprocessor will convert everything it finds to text in the least surprising way possible.
Invoke the Markdown dialect by using @code{#lang pollen/markdown} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" default-markdown-source-ext)}. These forms are equivalent:
Invoke the Markdown dialect by using @code{#lang pollen/markdown} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" pollen-markdown-source-ext)}. These forms are equivalent:
@racketmod[#:file "sample.txt.pmd" pollen
@racketmod[#:file "sample.txt.pmd" pollen
@ -112,9 +110,9 @@ _...source...
The output of the Markdown dialect, provided by @racket[doc], is a tagged X-expression.
The output of the Markdown dialect, provided by @racket[doc], is a tagged X-expression.
Invoke the Pollen markup dialect by using @code{#lang pollen/markup} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" default-markup-source-ext)}. These forms are equivalent:
Invoke the Pollen markup dialect by using @code{#lang pollen/markup} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" pollen-markup-source-ext)}. These forms are equivalent:
@racketmod[#:file "about.html.pm" pollen
@racketmod[#:file "about.html.pm" pollen
@ -127,10 +125,10 @@ _...source...
The output of the Pollen markup dialect, provided by @racket[doc], is a tagged X-expression.
The output of the Pollen markup dialect, provided by @racket[doc], is a tagged X-expression.
Invoke the pagetree dialect by using @code{#lang pollen/ptree} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" default-pagetree-source-ext)}. These forms are equivalent:
Invoke the pagetree dialect by using @code{#lang pollen/ptree} as the first line of your source file, or by using @code{#lang pollen} with a file extension of @code{@(format ".~a" pollen-pagetree-source-ext)}. These forms are equivalent:
@racketmod[#:file "main.ptree" pollen
@racketmod[#:file "main.ptree" pollen
@ -154,16 +152,16 @@ These aren't source formats because they don't contain a @tt{#lang pollen} line.
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}.
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 if 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.
The null extension is also useful for templates — @filepath{template.html} and @filepath{template.html.p} will work the same way.
@ -177,4 +175,4 @@ This convention occasionally flummoxes other programs that assume a file can onl
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).
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 (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{/}).
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{/}).
@ -25,26 +25,13 @@ After the initial download, Pollen does not require a network connection.
@item{@link["http://download.racket-lang.org/"]{Download and install Racket}, which includes DrRacket. (Of course, you're welcome to use your preferred text editor, but the tutorials will assume you're using DrRacket.)}
@item{@link["http://download.racket-lang.org/"]{Download and install Racket}, which includes DrRacket. (Of course, you're welcome to use your preferred text editor, but the tutorials will assume you're using DrRacket.)}
@item{@bold{Windows} users: when you see instructions that reference @exec{racket} or @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{Update the @envvar{PATH} environment variable on your system to include the directory that holds the racket application. On @bold{Mac OS} and @bold{Linux}, this path will be something like @filepath{/path/to/racket/bin}. On @bold{Windows}, it’ll be something like @filepath{C:\Program Files\Racket}. Then, from the terminal, you’ll be able to run @exec{racket} and @exec{raco} (see @other-doc['(lib "scribblings/raco/raco.scrbl")]).}
@item{@bold{Linux} and @bold{Mac OS} users: update your system @envvar{PATH} variable to include the path to Racket's newly installed @filepath{bin} directory. Then, from the terminal, you'll be able to run @exec{racket} and @exec{raco} (see @other-doc['(lib "scribblings/raco/raco.scrbl")]).}
@item{@bold{Windows} users who haven’t altered your @envvar{PATH} before: don’t panic. To add the Racket command-line programs to your Windows 10 @envvar{PATH}, click the Windows search box, type the word @exec{path}, and then click on @onscreen{Edit the system environment variables}. Click on the @onscreen{Environment Variables} button. In the top window, which contains your user variables, find @exec{Path} and double-click it to open. Click the @onscreen{New} button and either use the @onscreen{Browse} button to select your Racket directory, or manually enter its path. Restart your Windows terminal (either the Command Prompt or PowerShell) and now @exec{racket} and @exec{raco} should work.
@item{@bold{Mac OS} users who haven't altered your @envvar{PATH} before: don't panic. You need to add the full path of Racket's @filepath{bin} subdirectory to the @filepath{/etc/paths} file on your system.
First, confirm that your Racket installation works. Open your new Racket directory and launch DrRacket. If DrRacket works, then your Racket installation is sound.
@item{@bold{Mac OS} users who haven't altered your @envvar{PATH} before: don't panic. Follow @link["https://beautifulracket.com/setting-the-mac-os-path.html"]{these instructions}.}
Second, verify the full path to the @filepath{bin} subdirectory of your new Racket directory. If you put this directory in @filepath{Applications} as recommended, the path will be:
Third, copy the terminal command below, paste it into your terminal, and type return. (It will ask you for your password, because the @filepath{paths.d} directory is restricted to administrators.)
@terminal{sudo sh -c 'echo "/Applications/Racket v@(short-version)/bin" >> /etc/paths.d/racket'
}
Of course, @filepath{/Applications/Racket v@(short-version)/bin} in this command should be edited as necessary to represent the actual location and version of your Racket installation.}
@item{@bold{Mac OS} users who are still confused: here's an @link["https://beautifulracket.com/setting-the-mac-os-path.html"]{even gentler walkthrough}.}
@item{@bold{Linux}, @bold{Mac OS}, and @bold{Windows} users: try typing @exec{racket} on your command line, and you should see something like this:
@item{@bold{Linux}, @bold{Mac OS}, and @bold{Windows} users: try typing @exec{racket} on your command line, and you should see something like this:
@ -111,5 +98,58 @@ In general, I subscribe to the view that software should let you do what you wan
I've been using Pollen daily for several years (and will continue to do so, because my main work is writing). I've made Pollen available because a) I'm certain that others have had the same frustrations that I have, and b) feature suggestions and bug reports make it more useful for everyone.
I've been using Pollen daily for several years (and will continue to do so, because my main work is writing). I've made Pollen available because a) I'm certain that others have had the same frustrations that I have, and b) feature suggestions and bug reports make it more useful for everyone.
I hope you enjoy using it. If you get stuck on something not covered here, see @secref["Getting_more_help" #:doc '(lib "pollen/scribblings/pollen.scrbl")].
I hope you enjoy using it.
@section{Getting more help}
@subsection{Bugs and feature requests}
Can be submitted as @link["https://git.matthewbutterick.com/mbutterick/pollen/issues"]{issues} at the main Pollen source repository.
@subsection{Questions & discussion}
For general tips and how-to questions, use the @link["https://forums.matthewbutterick.com/c/typesetting/"]{Pollen discussion forum}. I'll also use that list to post major changes and new features. You need an account to post (free and easy to set up with an email address).
(BTW, the former ``pollenpub'' Google Group and the ``pollen-users'' GitHub repo are now deprecated.)
@subsection{Can I see the source for @italic{Practical Typography} or @italic{Typography for Lawyers}?}
Yes, a tutorial project based on the previous version of @link["http://typographyforlawyers.com/"]{@italic{Typography for Lawyers}} is available by installing the @link["https://docs.racket-lang.org/pollen-tfl/"]{pollen-tfl} package the same way you installed Pollen.
The current versions of @italic{Practical Typography} & @italic{Typography for Lawyers} are generated from a single set of Pollen source files, which is a complication that makes them less suitable for an introductory tutorial. Still, even though this tutorial project is based on an earlier version, the coding techniques are very close to what I still use. Learn with confidence.
@subsection{Utilities & libraries}
@link["https://github.com/malcolmstill/pollen-count"]{pollen-count}: enumeration and cross-referencing library by Malcolm Still
@link["https://github.com/lijunsong/pollen-mode"]{pollen-mode}: Emacs mode for Pollen by Junsong Li
@link["https://github.com/basus/pollen-mode"]{Pollen mode}: Emacs mode for Pollen by Shrutarshi Basu
@link["https://docs.racket-lang.org/pollen-component/"]{Pollen Component}: Component-based development for Pollen by Leandro Facchinetti
@link["https://docs.racket-lang.org/css-expr/"]{CSS-expressions}: S-expression-based CSS by Leandro Facchinetti
@link["https://github.com/lijunsong/pollen-rock"]{Pollen Rock}: rendering server and an in-browser editor for Pollen
@link["https://github.com/appliedsciencestudio/talks/tree/master/mxnet"]{Polllen as a front end for Reveal.js} by Dave Liepmann. Reveal.js is a library that allows you to create slide presentations in pure HTML/CSS that run in the browser.
@subsection{More projects & guides}
@link["https://digitalwords.net"]{Digital Words} by Júda Ronén [@link["https://gitlab.com/rwmpelstilzchen/digitalwords.net"]{source}]
@link["https://thelocalyarn.com/excursus/secretary"]{Secretary of Foreign Relations} by Joel Dueck [@link["https://github.com/otherjoel/try-pollen"]{source}]
@link["https://github.com/fasiha/pollen-guide"]{A Poor Guide to Pollen} by Ahmed Fasih
@link["https://youtu.be/20GGVNBykaw"]{The World's Most Dangerous Racket Programmer} and @link["https://youtu.be/IMz09jYOgoc"]{Like a Blind Squirrel in a Ferrari}: short talks about Pollen that I gave at RacketCons 2013 and 2014, respectively.
@link["http://mstill.io"]{mstill.io blog} by Malcolm Still [@link["https://github.com/malcolmstill/mstill.io"]{source}]
Can be submitted as @link["https://github.com/mbutterick/pollen/issues"]{GitHub issues}.
@section{Mailing list}
For general tips and how-to questions, use the @link["https://groups.google.com/forum/#!forum/pollenpub"]{Pollen discussion group} at @link["mailto:pollenpub@googlegroups.com"]{pollenpub@"@"googlegroups.com}. I'll also use that list to post major changes and new features.
@section{Utilities & libraries}
@link["https://github.com/malcolmstill/pollen-count"]{pollen-count}: enumeration and cross-referencing library by Malcolm Still
@link["https://github.com/lijunsong/pollen-mode"]{pollen-mode}: Emacs mode for Pollen by Junsong Li
@link["https://github.com/basus/pollen-mode"]{Pollen mode}: Emacs mode for Pollen by Shrutarshi Basu
@link["https://docs.racket-lang.org/pollen-component/"]{Pollen Component}: Component-based development for Pollen by Leandro Facchinetti
@link["https://docs.racket-lang.org/css-expr/"]{CSS-expressions}: S-expression-based CSS by Leandro Facchinetti
@link["https://github.com/lijunsong/pollen-rock"]{Pollen Rock}: rendering server and an in-browser editor for Pollen
@section{Can I see the source for Practical Typography or Typography for Lawyers?}
Yes, the source for @link["http://typographyforlawyers.com/"]{Typography for Lawyers} is available. In terms of content, TFL was originally the basis of Practical Typography. But in terms of code, this new TFL website is essentially a clone of Practical Typography, but rewritten to be clearer and more instructive, with extensive source comments. [@link["https://github.com/mbutterick/pollen-tfl"]{source}]
@section{More projects & guides}
@link["http://mstill.io"]{mstill.io blog} by Malcolm Still [@link["https://github.com/malcolmstill/mstill.io"]{source}]
@link["https://thelocalyarn.com/excursus/secretary"]{Secretary of Foreign Relations} by Joel Dueck [@link["https://github.com/otherjoel/try-pollen"]{source}]
@link["https://github.com/fasiha/pollen-guide"]{A Poor Guide to Pollen} by Ahmed Fasih
@link["https://youtu.be/20GGVNBykaw"]{The World's Most Dangerous Racket Programmer} and @link["https://youtu.be/IMz09jYOgoc"]{Like a Blind Squirrel in a Ferrari}: short talks about Pollen that I gave at RacketCons 2013 and 2014, respectively.
@link["https://www.leafac.com/"]{Leandro Facchinetti's personal website} [@link["https://github.com/leafac/www.leafac.com/tree/pollen"]{source}]
@ -24,7 +24,7 @@ Pagetrees surface throughout the Pollen system. They're primarily used for navig
@section{Making pagetrees with a source file}
@section{Making pagetrees with a source file}
A pagetree source file either starts with @code{#lang pollen} and uses the @racketfont{@(format ".~a" default-pagetree-source-ext)} extension, or starts with @code{#lang pollen/ptree} and then can have any file extension.
A pagetree source file either starts with @code{#lang pollen} and uses the @racketfont{@(format ".~a" pollen-pagetree-source-ext)} extension, or starts with @code{#lang pollen/ptree} and then can have any file extension.
Unlike other Pollen source files, since the pagetree source is not rendered into an output format, the rest of the filename is up to you.
Unlike other Pollen source files, since the pagetree source is not rendered into an output format, the rest of the filename is up to you.
@ -436,7 +436,7 @@ Return the pagenode immediately after @racket[_p]. For @racket[next*], return al
[pagetree-source (or/c pagetree? pathish?)])
[pagetree-source (or/c pagetree? pathish?)])
pagetree?
pagetree?
]
]
Get a pagetree from a @ext[default-pagetree-source-ext] source file, namely @racket[_pagetree-source]. If @racket[_pagetree-source] is already a pagetree, just pass it through.
Get a pagetree from a @ext[pollen-pagetree-source-ext] source file, namely @racket[_pagetree-source]. If @racket[_pagetree-source] is already a pagetree, just pass it through.
Use Pygments to highlight the code in @racket[_lines-of-code] according to the semantics of @racket[_language]. Does not apply any visual styling, just the markup.
Use Pygments to highlight the code in @racket[_lines-of-code] according to the semantics of @racket[_language]. Does not apply any visual styling, just the markup. The optional @racket[_python-executable] controls which Python Pollen should use (which will be useful when you install Pygments in a non-default version of Python). The optional @racket[_line-numbers?] controls whether or not the syntax highlight should include line numbers. The optional @racket[_css-class] controls the CSS class name for the syntax highlight. The optional @racket[_lines] will tag the specified lines of code with the CSS class @tt{hll}.
@ -17,6 +17,7 @@ Start a new document. Change the top line to:
The first line of every Pollen source file will start with @code{#lang pollen}.
The first line of every Pollen source file will start with @code{#lang pollen}.
@margin-note{If you prefer to use the command line and an editor other than DrRacket, equivalent command-line instructions follow at the end of each section. If you're using DrRacket, you can ignore these.}
@section{Running a source file}
@section{Running a source file}
@ -59,6 +60,11 @@ document.write('Hello world');
printf("Hello world");
printf("Hello world");
}
}
@bold{Command line}: save the file as @filepath{hello.txt.pp} and then run:
@terminal{
> racket hello.txt.pp
}
@section{Naming, saving, and rendering a source file}
@section{Naming, saving, and rendering a source file}
@ -107,7 +113,11 @@ The old @filepath{hello.txt} will be replaced with a new one showing your change
You just saw two ways to view the output of a Pollen source file —first, you ran it in DrRacket. Second, you rendered it to an output file.
You just saw two ways to view the output of a Pollen source file —first, you ran it in DrRacket. Second, you rendered it to an output file.
Now here's a third: the Pollen project server. To start the project server, return to your terminal and issue two commands:
Now here's a third: the Pollen project server.
@margin-note{The project server is a real web server running on your machine. By default it will respond to requests from any computer. Use the @exec{--local} switch with this command to restrict the project server to responding to requests from localhost. See @secref["raco_pollen_start"].}
To start the project server, return to your terminal and issue two commands:
@terminal{
@terminal{
> cd /directory/containing/your/hello-file
> cd /directory/containing/your/hello-file
@ -151,6 +161,12 @@ Paradise Theatre}
Notice what happened —the Pollen project server dynamically regenerated the output file (@filepath{hello.txt}) from the source file (@filepath{hello.txt.pp}) after you edited the source. If you like, try making some more changes to @filepath{hello.txt.pp}, and reloading the browser to see the updates in @filepath{hello.txt}. The project server will regenerate the file whenever it changes.
Notice what happened —the Pollen project server dynamically regenerated the output file (@filepath{hello.txt}) from the source file (@filepath{hello.txt.pp}) after you edited the source. If you like, try making some more changes to @filepath{hello.txt.pp}, and reloading the browser to see the updates in @filepath{hello.txt}. The project server will regenerate the file whenever it changes.
@bold{Command line}: to see the changes after each edit, run:
@terminal{
> raco pollen render hello.txt.pp
> cat hello.txt
}
@section{Intermission}
@section{Intermission}
@ -208,6 +224,8 @@ In your web browser, reload @link["http://localhost:8080/margin.html"]{@filepath
Still, this is the tiniest tip of the iceberg. The Pollen preprocessor gives you access to everything in the Racket programming language —including string manipulation, math functions, and so on.
Still, this is the tiniest tip of the iceberg. The Pollen preprocessor gives you access to everything in the Racket programming language —including string manipulation, math functions, and so on.
@bold{Command line}: type the lozenge using whatever keyboard method you prefer (see @secref["the-lozenge"] for suggestions).
@section{Markdown mode}
@section{Markdown mode}
When used as a preprocessor, Pollen's rule is that what you write is what you get. But if you're targeting HTML, who wants to type out all those @code{<tedious>tags</tedious>}? You can make Pollen do the heavy lifting by using an @defterm{authoring mode}.
When used as a preprocessor, Pollen's rule is that what you write is what you get. But if you're targeting HTML, who wants to type out all those @code{<tedious>tags</tedious>}? You can make Pollen do the heavy lifting by using an @defterm{authoring mode}.
@ -273,6 +291,13 @@ Pollen is handling three tasks here: interpreting the commands in the source, co
But what if you wanted to use Pollen as a preprocessor that outputs a Markdown file? No problem —just change the source name from @filepath{downtown.html.pmd} to @filepath{downtown.md.pp}. Changing the extension from @filepath{.pmd} to @filepath{.pp} switches Pollen from Markdown mode back to preprocessor mode. And changing the base name from @filepath{downtown.html} to @filepath{downtown.md} updates the name of the output file (and thereby skips the HTML conversion).
But what if you wanted to use Pollen as a preprocessor that outputs a Markdown file? No problem —just change the source name from @filepath{downtown.html.pmd} to @filepath{downtown.md.pp}. Changing the extension from @filepath{.pmd} to @filepath{.pp} switches Pollen from Markdown mode back to preprocessor mode. And changing the base name from @filepath{downtown.html} to @filepath{downtown.md} updates the name of the output file (and thereby skips the HTML conversion).
@bold{Command line}: to render the new source file after each edit, run:
@terminal{
> raco pollen render downtown.html.pmd
}
And then view the resulting @filepath{downtown.html} file however you like.
@section{Pollen markup}
@section{Pollen markup}
@ -333,6 +358,14 @@ Return to the @link["http://localhost:8080/index.ptree"]{project dashboard} and
Pollen markup takes a little more effort to set up. But it also allows you more flexibility. If you want to do semantic markup, or convert your source into @seclink["fourth-tutorial"]{multiple output formats}, or handle complex page layouts —it's the way to go. (For more, see @seclink["Writing_with_Pollen_markup"
Pollen markup takes a little more effort to set up. But it also allows you more flexibility. If you want to do semantic markup, or convert your source into @seclink["fourth-tutorial"]{multiple output formats}, or handle complex page layouts —it's the way to go. (For more, see @seclink["Writing_with_Pollen_markup"
#:doc '(lib "pollen/scribblings/pollen.scrbl")].)
#:doc '(lib "pollen/scribblings/pollen.scrbl")].)
@bold{Command line}: to render the new source file after each edit, run:
@terminal{
> raco pollen render uptown.html.pm
}
And then view the resulting @filepath{uptown.html} file however you like.
@section{Templates}
@section{Templates}
The HTML pages we just made looked pretty dull. For the last stop on the quick tour, let's fix that.
The HTML pages we just made looked pretty dull. For the last stop on the quick tour, let's fix that.
@ -341,7 +374,7 @@ Pollen source files that are written in an authoring mode (i.e., @filepath{.pmd}
When it needs a template, Pollen first looks for a file in the project directory named @filepath{template.[output extension of source]}. Thus, for @filepath{uptown.html.pm}, the output extension will be @filepath{.html}, and Pollen will first look for @filepath{template.html}.
When it needs a template, Pollen first looks for a file in the project directory named @filepath{template.[output extension of source]}. Thus, for @filepath{uptown.html.pm}, the output extension will be @filepath{.html}, and Pollen will first look for @filepath{template.html}.
So let's create @filepath{template.html}. Make a new file that with the following lines and save it to the same directory as @filepath{uptown.html.pm}:
So let's create @filepath{template.html}. Make a new file with the following lines and save it to the same directory as @filepath{uptown.html.pm}:
@fileblock["template.html"
@fileblock["template.html"
@codeblock{
@codeblock{
@ -359,6 +392,14 @@ This is a simple HTML file that should look familiar, except for the two templat
Return to your web browser and reload @link["http://localhost:8080/uptown.html"]{@filepath{uptown.html}}. (Or @link["http://localhost:8080/downtown.html"]{@filepath{downtown.html}} —both will work.) The page will be rendered with the new @filepath{template.html}. As before, you can edit the template or the source and the project server will dynamically update the output file.
Return to your web browser and reload @link["http://localhost:8080/uptown.html"]{@filepath{uptown.html}}. (Or @link["http://localhost:8080/downtown.html"]{@filepath{downtown.html}} —both will work.) The page will be rendered with the new @filepath{template.html}. As before, you can edit the template or the source and the project server will dynamically update the output file.
@bold{Command line}: to refresh the output after each edit of @filepath{template.html}, run:
@ -49,7 +49,8 @@ Displays a list of available commands.
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]).
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, and one optional switch.
This command can be invoked with two optional arguments, and two optional switches.
@racket[raco pollen start _path] will start the project server from @racket[_path] rather than the current directory (making @racket[_path] its root 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).
@ -67,6 +68,10 @@ If you want to start in the current directory but with a different port, use @li
@terminal{
@terminal{
> raco pollen start . 8088}
> raco pollen start . 8088}
@margin-note{Pollen defaults to port @id[default-project-server-port] because it's not commonly used by other network services. But Pollen has no idea what else is running on your machine. If @id[default-project-server-port] is already in use, you'll get an error when you try to start the Pollen project server. In that case, try a different port.}
Adding the optional @exec{-l} or @exec{--launch} switch will open the main project dashboard in your web browser after the project server starts.
Adding the optional @exec{-l} or @exec{--launch} switch will open the main project dashboard in your web browser after the project server starts.
Adding the optional @exec{--local} switch will restrict the project server to responding to requests from localhost. (By default, the project server will respond to requests from any client.)
Adding the optional @exec{--local} switch will restrict the project server to responding to requests from localhost. (By default, the project server will respond to requests from any client.)
@ -76,6 +81,14 @@ Adding the optional @exec{--local} switch will restrict the project server to re
This command can be invoked two ways: in source mode or directory mode.
This command can be invoked two ways: in source mode or directory mode.
In both modes, the optional @exec{--dry-run} or @exec{-d} switch prints the paths that would be rendered by this command without actually doing so.
In both modes, the optional @exec{--force} or @exec{-f} switch forces a fresh render from source, even if the file is already cached, by updating the modification date of the file (à la @exec{touch}). Thus, if modification dates are important to you, don't use this option.
In both modes, the optional @exec{--null} or @exec{-n} switch renders as usual, but doesn't write any files. (Convenient if you're arranging special render behavior, for instance writing to a database or network server.)
@bold{Source mode}: @racket[raco pollen render _source...] will render only the source paths specified in @racket[_source ...]. Consistent with the usual command-line idiom, this can be a single path, a list of paths, or a pattern:
@bold{Source mode}: @racket[raco pollen render _source...] will render only the source paths specified in @racket[_source ...]. Consistent with the usual command-line idiom, this can be a single path, a list of paths, or a pattern:
@terminal{
@terminal{
@ -89,20 +102,39 @@ Paths can also be specified as output rather than input paths, and the correspon
> raco pollen render foo.html
> raco pollen render foo.html
> raco pollen render foo.html bar.html zam.css}
> raco pollen render foo.html bar.html zam.css}
The optional @exec{-t} or @exec{--target} switch specifies the render target for multi-output source files. If the target is omitted, the renderer will use whatever target appears first in @racket[(setup:poly-targets)].
If a pagetree file is included in @racket[_source], all the files it lists will be rendered using the above rules.
The optional @exec{--target} or @exec{-t} switch specifies the render target to use for multi-output source files. (Files of other types encountered in @racket[_source] will still be rendered as usual.) If the target is omitted, the renderer will use whatever target appears first in @racket[(setup:poly-targets)].
@terminal{
@terminal{
> raco pollen render -t pdf foo.poly.pm}
> raco pollen render -t pdf foo.poly.pm}
See also @seclink["raco-pollen-render-poly"].
See also @seclink["raco-pollen-render-poly"].
@italic{Warning}: In all cases, the newly rendered output file will overwrite any previous output file.
The optional @exec{--parallel} or @exec{-p} switch creates a set of parallel rendering jobs equal to the number of processing cores on the system. On a multi-core machine, this will usually make your rendering job finish faster. The order of rendering is not guaranteed, of course, so if your project depends on a certain order of rendering, don't use this option.
@terminal{
> raco pollen render -p foo.html bar.html zam.css
}
The alternative @exec{--jobs <count>} or @exec{-j <count>} switch does the same thing, but takes one argument that creates @racket[<count>] parallel jobs (which can be more or less than the number of processing cores).
As a rule of thumb, parallel rendering works best if you do @exec{raco setup} first, which updates Pollen's disk caches:
@terminal{
> raco setup -p
> raco pollen render -p
}
@italic{Warning}: In all cases, the newly rendered output file will overwrite any previous output file.
@bold{Directory mode}: @racket[raco pollen render _directory] renders all preprocessor source files and then all pagetree files found in the specified directory. If none of these files are found, a pagetree will be generated for the directory (which will include all source files) and then rendered. If the @racket[_directory] argument is omitted, the command defaults to the current directory.
@bold{Directory mode}: @racket[raco pollen render _directory] renders all preprocessor source files and then all pagetree files found in the specified directory. If none of these files are found, a pagetree will be generated for the directory (which will include all source files, but also everything else that exists there; see @secref["The_automatic_pagetree"]) and then rendered. If the @racket[_directory] argument is omitted, the command defaults to the current directory.
In directory mode, this command can be invoked with two other optional arguments (in addition to the @exec{--target} switch mentioned above):
In directory mode, this command can be invoked with two other optional arguments (in addition to the @exec{--target}, @exec{--parallel}, and @exec{--jobs} switches mentioned above):
The @exec{--subdir} or @exec{-s} switch also renders subdirectories. @racket[current-project-root] remains fixed at the initial directory, just as it would be in the project server after invoking @racket[raco pollen start].
The @exec{--subdir} or @exec{-s} switch also renders subdirectories. @racket[current-project-root] remains fixed at the initial directory, just as it would be in the project server after invoking @racket[raco pollen start].
@ -129,12 +161,29 @@ You can determine the default publishing destination for a project by overriding
Certain files and directories are automatically omitted from the published directory, including Racket and Pollen sources, Pollen caches, and source-control directories (like @tt{.git} and @tt{.svn}). You can omit other files by overriding @racket[default-omitted-path?]. You can override these omissions —that is, force a path to be published —by overriding @racket[default-extra-path?].
Certain files and directories are automatically omitted from the published directory, including Racket and Pollen sources, Pollen caches, and source-control directories (like @tt{.git} and @tt{.svn}). You can omit other files by overriding @racket[default-omitted-path?]. You can override these omissions —that is, force a path to be published —by overriding @racket[default-extra-path?].
The optional @exec{--dry-run} or @exec{-d} switch prints the source and destination directories for publishing without actually doing so. If the destination-directory path cannot be created, an error will arise.
@section{@exec{raco pollen setup}}
@section{@exec{raco pollen setup}}
Finds Pollen source files in the current directory, compiles them, and loads the results into the @seclink["Cache" #:doc '(lib "pollen/scribblings/pollen.scrbl")]. This will give you the snappiest performance during an interactive session with the project server.
Finds Pollen source files in the current directory, compiles them, and loads the results into the @seclink["Cache" #:doc '(lib "pollen/scribblings/pollen.scrbl")]. This will give you the snappiest performance during an interactive session with the project server.
Can also be invoked as @racket[raco pollen setup _directory], which will set up a different project @racket[_directory].
Can also be invoked as @racket[raco pollen setup _directory], which will set up the files in @racket[_directory].
The optional @exec{--parallel} or @exec{-p} switch creates a set of parallel setup jobs equal to the number of processing cores on the system. On a multi-core machine, this will usually make your setup finish faster.
@terminal{
> raco pollen setup -p
}
The alternative @exec{--jobs <count>} or @exec{-j <count>} switch does the same thing, but takes one argument that creates @racket[<count>] parallel jobs (which can be more or less than the number of processing cores).
@terminal{
> raco pollen setup -j 4
}
@margin-note{As of mid-2020, Pollen's parallel-processing performance under the CS (= Chez Scheme) variant of Racket is worse than ordinary Racket. If you use Racket CS, you may get better results using @exec{-j 4} (which will limit the operation to four cores) than @exec{-p} (which will use all available cores).}
The optional @exec{--dry-run} or @exec{-d} switch prints the paths that would be compiled by this command without actually doing so.
@section{@exec{raco pollen reset}}
@section{@exec{raco pollen reset}}
@ -171,4 +220,37 @@ Result is DEBUG
}
}
@section{Logging & the @exec{PLTSTDERR} environment variable}
@margin-note{See @secref["logging" #:doc '(lib "scribblings/reference/reference.scrbl")] for an introduction to Racket's logging system.}
By default, Pollen will log messages at the @racket['info] level or above to the console during any terminal session (e.g., project server or rendering job). So if you start the project server like so:
@terminal{
> raco pollen start
}
You will see log messages starting with:
@terminal{
pollen: starting project server ...
}
And so forth.
You can use Racket's @racket[PLTSTDERR] environment variable to adjust the level of logging. If you provide an explicit log level for Pollen, it will override this default behavior. So if you only want to see messages at the @racket['error] level or above, you would invoke the project server like so:
@terminal|{
> PLTSTDERR=error@pollen raco pollen start
}|
After this, the project server will work normally, but you won't see the usual @racket['info]-level messages, and instead will only see @racket['error] messages or above.
Conversely, if you want more detailed logging, you can invoke the @racket['debug] log level like so:
@terminal|{
> PLTSTDERR=debug@pollen raco pollen start
}|
Then you'll see the usual @racket['info] messages, plus a bunch more.
Renders @racket[_source-path]. The rendering behavior depends on the type of source file (for details, see @secref["File_formats" #:doc '(lib "pollen/scribblings/pollen.scrbl")]):
Renders @racket[_source-path]. The rendering behavior depends on the type of source file (for details, see @secref["File_formats" #:doc '(lib "pollen/scribblings/pollen.scrbl")]):
A @racketmodname[pollen/pre] file is rendered without a template.
A @racketmodname[pollen/pre] file is rendered without a template.
@ -75,7 +75,7 @@ Note that @racket[_pt-or-pt-source] is used strictly as a list of files to rende
Find a template file for @racket[_source-path], with the following priority:
Find a template file for @racket[_source-path], with the following priority:
@itemlist[#:style 'ordered
@itemlist[#:style 'ordered
@item{If the @racket[metas] for @racket[_source-path] have a key for @code[(format "~a" default-template-meta-key)], then use the value of this key, e.g. —
@item{If the @racket[metas] for @racket[_source-path] have a key for @code[(format "~a" pollen-template-meta-key)], then use the value of this key, e.g. —
@code{◊(define-meta template "my-template.html")}
@code{◊(define-meta template "my-template.html")}
@ -88,7 +88,7 @@ If your project has @seclink["fourth-tutorial"]{multiple output targets}, you ca
}
}
@item{If this key doesn't exist, or refers to a nonexistent file, look for a default template with the name @code[(format "~a.[output extension]" default-template-prefix)]. Meaning, if @racket[_source-path] is @code[(format "intro.html.~a" default-markup-source-ext)], the output path would be @code["intro.html"], so the default template would be @code[(format "~a.html" default-template-prefix)]. Look for this default template in the same directory as the source file, and then search upwards within successive parent directories. (Corollary: a default template in the project root will apply to all files in the project unless overridden within a subdirectory.)}
@item{If this key doesn't exist, or refers to a nonexistent file, look for a default template with the name @code[(format "~a.[output extension]" pollen-template-prefix)]. Meaning, if @racket[_source-path] is @code[(format "intro.html.~a" pollen-markup-source-ext)], the output path would be @code["intro.html"], so the default template would be @code[(format "~a.html" pollen-template-prefix)]. Look for this default template in the same directory as the source file, and then search upwards within successive parent directories. (Corollary: a default template in the project root will apply to all files in the project unless overridden within a subdirectory.)}
@item{If this file doesn't exist, use the fallback template as a last resort. (See @secref["Templates"
@item{If this file doesn't exist, use the fallback template as a last resort. (See @secref["Templates"
@ -55,23 +71,6 @@ Every @racket[setup:]@racket[_name] function will resolve the current value of t
Determines the default HTTP port for the project server.}
Determines the default HTTP port for the project server.}
@defoverridable[main-export symbol?]{The main X-expression exported from a compiled Pollen source file.}
@defoverridable[meta-export symbol?]{The meta hashtable exported from a compiled Pollen source file.}
@defoverridable[extension-escape-char char?]{Character for escaping output-file extensions within source-file names.}
@deftogether[(
@defoverridable[preproc-source-ext symbol?]
@defoverridable[markup-source-ext symbol?]
@defoverridable[markdown-source-ext symbol?]
@defoverridable[null-source-ext symbol?]
@defoverridable[pagetree-source-ext symbol?]
@defoverridable[template-source-ext symbol?]
@defoverridable[scribble-source-ext symbol?]
)]{File extensions for Pollen source files.}
@defoverridable[main-pagetree string?]{Pagetree that Pollen dashboard loads by default in each directory.}
@defoverridable[main-pagetree string?]{Pagetree that Pollen dashboard loads by default in each directory.}
@ -89,8 +88,6 @@ Determines the default HTTP port for the project server.}
@defoverridable[command-char char?]{The magic character that indicates a Pollen command, function, or variable.}
@defoverridable[command-char char?]{The magic character that indicates a Pollen command, function, or variable.}
@defoverridable[template-prefix string?]{Prefix of the default template.}
@deftogether[(
@deftogether[(
@(defoverridable newline string?)
@(defoverridable newline string?)
@ -108,40 +105,43 @@ Default separators used in decoding.
@defoverridable[cache-watchlist (listof (or/c path? path-string?))]{List of extra files that the cache (= render cache + compile cache, collectively) watches during a project-server session. If one of the files on the watchlist changes, the cache is invalidated (just as it would be if @racket["pollen.rkt"] changed).
@defoverridable[cache-watchlist (listof (or/c path? path-string?))]{List of extra files that the cache (= render cache + compile cache, collectively) watches during a project-server session. If one of the files on the watchlist changes, the cache is invalidated (just as it would be if @racket["pollen.rkt"] changed).
If the cache can't find a certain file on the watchlist, it will be ignored. Therefore, to avoid unexpected behavior, the best policy is to pass in complete paths (or path strings). An easy way to convert a module name into a complete path is with @racket[resolve-module-path]:
If the cache can't find a certain file on the watchlist, no error will arise. The file will simply be ignored. Therefore, to avoid unexpected behavior, the best policy is to use complete paths (or path strings). One way to generate a complete path to a local file is with @racket[define-runtime-path]. Another way, if you're using a module that's already installed as part of a package, is with @racket[resolve-module-path]:
@defoverridable[envvar-watchlist (listof string?)]{List of extra environment variables that are used in cache keys. Separate caches will be maintained for each distinct value of an environment variable. @secref["The_POLLEN_environment_variable"] is always used, regardless of how this value is set.
@defoverridable[publish-directory (or/c path-string? path-for-some-system?)]{Default target for @secref{raco_pollen_publish}. A complete path is used as is; a relative path is published to the desktop.. @pollen-history[#:added "1.1"]}
Both the names and the values of environment variables are case-insensitive, so @racket["PUB"] and @racket["pub"] and @racket["pUb"] are all treated the same.
@defoverridable[omitted-path? (path? . -> . boolean?)]{Predicate that determines whether a path is omitted from @secref{raco_pollen_render} and @secref{raco_pollen_publish} operations. If the predicate evaluated to @racket[#t], then the path is omitted.
@pollen-history[#:added "1.1"]}
@defoverridable[publish-directory (or/c path-string? path-for-some-system?)]{Default target for @secref{raco_pollen_publish}. A complete path is used as is; a relative path is published to the desktop.. @history[#:added "1.1"]}
@defoverridable[extra-path? (path? . -> . boolean?)]{Predicate that determines if path is rendered & published, overriding @racket[(setup:omitted-path?)] above, and Pollen's default publish settings. For instance, Pollen automatically omits files with a @racket[.rkt] extension. If you wanted to force a @racket[.rkt] file to be published, you could include it here.
@pollen-history[#:added "1.1"]}
@defoverridable[omitted-path? (path? . -> . boolean?)]{Predicate that determines whether a path is omitted from @secref{raco_pollen_render} and @secref{raco_pollen_publish} operations. If the predicate evaluated to @racket[#t], then the path is omitted.
@history[#:added "1.1"]}
@defoverridable[splicing-tag symbol?]{Key used to signal that an X-expression should be spliced into its containing X-expression.}
@defoverridable[extra-path? (path? . -> . boolean?)]{Predicate that determines if path is rendered & published, overriding @racket[(setup:omitted-path?)] above, and Pollen's default publish settings. For instance, Pollen automatically omits files with a @racket[.rkt] extension. If you wanted to force a @racket[.rkt] file to be published, you could include it here.
@defoverridable[poly-source-ext symbol?]{Extension that indicates a source file can target multiple output types.}
@history[#:added "1.1"]}
@defoverridable[poly-targets (listof symbol?)]{List of symbols that denotes the possible targets of a @racket['poly] source file.}
@defoverridable[poly-targets (listof symbol?)]{List of symbols that denotes the possible targets of a @racket['poly] source file.}
@ -151,9 +151,18 @@ If the cache can't find a certain file on the watchlist, it will be ignored. The
@defoverridable[trim-whitespace? boolean?]{Predicate that controls whether the Pollen source reader trims whitespace from the beginning of a @racket[doc] export. You might set this to @racket[#false] if you're using Pollen as a preprocessor for another programming language and you want to preserve leading whitespace accurately.
@defoverridable[trim-whitespace? boolean?]{Predicate that controls whether the Pollen source reader trims whitespace from the beginning of a @racket[doc] export. You might set this to @racket[#false] if you're using Pollen as a preprocessor for another programming language and you want to preserve leading whitespace accurately.
@pollen-history[#:added "1.5"]}
@history[#:added "1.5"]}
@defoverridable[allow-unbound-ids? boolean?]{Predicate that controls whether Pollen converts unbound identifiers into default tags by altering the behavior of @racket[#%top] in @racketmodname[pollen/top].
@history[#:added "2.0"]}
@defoverridable[external-renderer (or/c (list/c module-path? symbol?) #f)]{A module path and identifier (suitable for use with @racket[dynamic-require]) that provide a function for Pollen to call instead of @racket[render] when rendering files needed by the @seclink["Using_the_project_server"]{project server} or when running @secref["raco_pollen_render"]. The function must accept the same arguments as @racket[render-to-file] and should return the final output as a @tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{string} or @tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{byte string}. Pollen will always write this return value out to the output file for you.
Setting this value gives you full control over (and responsibility for) how Pollen converts the compiled @racketidfont{doc} and @racketidfont{metas} from source files into their final output. Your renderer should be able to handle any of Pollen’s @seclink["Source_formats"]{source formats} or @seclink["Utility_formats"]{utility formats}. The operation of Pollen’s @racket[render] function is not affected by setting this value, so your renderer can use it as a fallback.
@history[#:added "3.2"]}
@section{Parameters}
@section{Parameters}
I mean @italic{parameters} in the Racket sense, i.e. values that can be fed to @racket[parameterize].
I mean @italic{parameters} in the Racket sense, i.e. values that can be fed to @racket[parameterize].
@ -162,7 +171,7 @@ I mean @italic{parameters} in the Racket sense, i.e. values that can be fed to @
A parameter that sets the HTTP port for the project server.}
A parameter that sets the HTTP port for the project server.}
@defparam[current-project-root port path?]{
@defparam[current-project-root path path?]{
A parameter that holds the root directory of the current project (e.g., the directory where you launched @code{raco pollen start}).}
A parameter that holds the root directory of the current project (e.g., the directory where you launched @code{raco pollen start}).}
@ -16,7 +16,7 @@ That's no longer true. The web is now more than 20 years old. During that time,
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.
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 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.
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 manipulate 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,itis.
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,itis.
@ -77,7 +77,7 @@ Did it work? Sort of. Source code went in; web pages came out. But it was also c
@section{Enter Racket}
@section{Enter Racket}
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.
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 full-featured 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 @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.
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.
@ -43,7 +43,7 @@ The good news is that this behavior means you use any tag you want in your marku
(em "Bonjour")
(em "Bonjour")
]
]
The bad news is that you'll never get an ``undefined identifier'' error. These undefined identifiers will happily sail through and be converted to tags.
The bad news is that you'll never get an ``unbound identifier'' error. These unbound identifiers will happily sail through and be converted to tags.
@examples[
@examples[
(require pollen/top)
(require pollen/top)
@ -52,6 +52,8 @@ The bad news is that you'll never get an ``undefined identifier'' error. These u
(erm "Bonjour")
(erm "Bonjour")
]
]
@margin-note{If you prefer the ordinary Racket-style behavior where unbound identifiers raise an error, define @racket[setup:allow-unbound-ids?] in your project to be @racket[#false].}
This isn't a bug. It's just a natural consequence of how Pollen's @racket[#%top] works. It can, however, make debugging difficult sometimes. Let's suppose my markup depends on @racket[very-important-function], which I don't import correctly.
This isn't a bug. It's just a natural consequence of how Pollen's @racket[#%top] works. It can, however, make debugging difficult sometimes. Let's suppose my markup depends on @racket[very-important-function], which I don't import correctly.
@examples[
@examples[
@ -96,5 +98,4 @@ By adding @racket[def/c], we restore the usual behavior, guaranteeing that we ge
@ -32,7 +32,7 @@ As I mentioned in the @secref["big-picture"], Pollen is built using Racket, and
But if not, or if you're just a curious character:
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}.
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/index.html"]{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}.
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.
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.
@ -199,11 +199,11 @@ The goal of this whole endeavor was to derive multiple output files from one sou
@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:
@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:
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.
What we're doing here is converting the X-expression to text in a smarter way. We use @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.
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.
@ -304,7 +304,7 @@ Then a @filepath{template.ltx.p}:
@ -391,7 +391,7 @@ First, we use @filepath{template.pdf.p} rather than @filepath{template.pdf} for
A quick narrative of the rest:
A quick narrative of the rest:
@codeblock|{
@codeblock|{
◊(local-require racket/file racket/system)
◊(require racket/file racket/system)
}|
}|
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).
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).
@ -21,7 +21,7 @@ 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}
@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 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["https://beautifulracket.com/"]{@italic{Beautiful Racket}}. Links to the source are available at the bottom of the article.
@ -226,15 +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:
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:
(p "I am " (strong "so") " happy to be writing this."))}
'(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 compiles 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:
From what you learned in the last section, it should be evident that this X-expression will convert to HTML that looks like this:
<p>I am <strong>so</strong> happy to be writing this.</p></root>}
<root>
<h1 id="deep-thought">Deep Thought</h1>
<p>I am <strong>so</strong> happy to be writing this.</p>
</root>
}
``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[#:tag-prefixes '("tutorial-2")]{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.
``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[#:tag-prefixes '("tutorial-2")]{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.
@ -291,7 +298,7 @@ And two major differences:
@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).}
@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.}
To see how this works, let's return to the source file we started in the last section:
To see how this works, let's return to the source file we started in the last section:
@ -364,9 +371,6 @@ Finally, we need to include the X-expression from our source file. By convention
@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].
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].
``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:
``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:
@ -401,6 +405,9 @@ Beyond that, all we need to do make sure our template has the three key ingredie
In your project directory, create a new file called @filepath{template.html.p}:
In your project directory, create a new file called @filepath{template.html.p}:
@margin-note{If you're using DrRacket on Mac OS to save this file, it may insist on adding a @filepath{rkt} extension to the filename. If so, you can either correct the filename after you save the file, or instead use a different text editor to create @filepath{template.html.p}.}
@fileblock["template.html.p"
@fileblock["template.html.p"
@codeblock[#:keep-lang-line? #f]{
@codeblock[#:keep-lang-line? #f]{
#lang pollen
#lang pollen
@ -638,7 +645,7 @@ The automatic pagetree for this project is exactly what you see in the dashboard
@subsection{Adding navigation links to the template with @tt{here}}
@subsection{Adding navigation links to the template with @tt{here}}
Recall from earlier in the tutorial that the content of your source file is made available in the template through the special variable @code{doc}. Likewise, the name of the current source file is made available through the special variable @code{here}.
Recall from earlier in the tutorial that the content of your source file is made available in the template through the special variable @code{doc}. Likewise, the output name of the current source file is made available through the special variable @code{here}.
To make any navigation link —up, down, sideways — the general idea is that we use @code{here} as input to a pagetree-navigation function, which then looks up the answer in the current pagetree.
To make any navigation link —up, down, sideways — the general idea is that we use @code{here} as input to a pagetree-navigation function, which then looks up the answer in the current pagetree.
@ -659,7 +666,9 @@ The current page is called ◊|here|.
</html>
</html>
}]
}]
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?
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?
Notice that @code{here} is always an @italic{output} filename, on the idea that navigation naturally makes connections among output files, not source files. In this case, this conversion to output name means we lose the @filepath{pmd} extension. (As we'll see below, we'll also use these output filenames when we're @secref["Making_a_pagetree_file"].)
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]:
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]:
@ -195,7 +195,7 @@ Then you have two options for adding attributes. The verbose way corresponds to
Each key–value 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.
Each key–value 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.
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}:
But this is boring to type out, so Pollen also allows you to specify attributes in tag functions with Racket-style @seclink["keyword-args" #:doc '(lib "scribblings/guide/guide.scrbl")]{keyword arguments}:
@fileblock["article.html.pm" @codeblock{
@fileblock["article.html.pm" @codeblock{
#lang pollen
#lang pollen
@ -205,6 +205,8 @@ But this is boring to type out, so Pollen also allows you to specify attributes
In this form, each attribute name is prefixed with @litchar{#:}, indicating a keyword argument. As before, the attribute value is in quotation marks following the keyword name.
In this form, each attribute name is prefixed with @litchar{#:}, indicating a keyword argument. As before, the attribute value is in quotation marks following the keyword name.
@margin-note{This keyword notation will work by default with any tag. When you're making a custom tag function, use @racket[define-tag-function] (rather than the usual @racket[define]) if you want your tag function to support keyword notation the same way.}
Both of these forms will produce the same X-expression:
Both of these forms will produce the same X-expression:
@ -513,7 +515,7 @@ This will produce an error in DrRacket:
@errorblock{
@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)}
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.
This code would not, however, produce an error if it were being run as a Pollen preprocessor file, because the preprocessor automatically converts numbers to strings. If you'd like to verify this, change the suffix to @code{.pp} and run the file again.
@ -12,7 +12,7 @@ Inconsistent with this system, Pollen's version also appends a build number, whi
@section{Source code}
@section{Source code}
Pollen's source code is @link["http://github.com/mbutterick/pollen"]{available from this Git repo}. The @tt{MASTER} branch of the repo will always contain the most recent stable version.
Pollen's source code is @link["https://git.matthewbutterick.com/mbutterick/pollen/"]{available from this Git repo}. The @tt{MASTER} branch of the repo will always contain the most recent stable version.
Racket's @link["http://pkg.racket-lang.org"]{package catalog} relies on this branch, so if you get your updates with @tt{raco pkg update pollen}, you'll get the most recent updates from this branch.
Racket's @link["http://pkg.racket-lang.org"]{package catalog} relies on this branch, so if you get your updates with @tt{raco pkg update pollen}, you'll get the most recent updates from this branch.
@ -24,6 +24,49 @@ Beyond keeping the commit history available, I make no promise to maintain the p
@section{Changelog}
@section{Changelog}
@subsection{Version 3.2}
Added @racket[setup:external-renderer].
@subsection{Version 3.1}
Downgraded the following @racket[pollen/setup] values from configurable to fixed: @racket[here-path-key], @racket[extension-escape-char].
@subsection{Version 3.0}
Changed rendering model to share a namespace between sequential renders, improving speed.
Added @racket[--force] switch to @secref["raco_pollen_render" #:doc '(lib "pollen/scribblings/pollen.scrbl")].
Added @racket[--dry-run] switch to @secref["raco_pollen_publish" #:doc '(lib "pollen/scribblings/pollen.scrbl")].
Downgraded the following @racket[pollen/setup] values from configurable to fixed: @racket[splicing-tag], @racket[preproc-source-ext], @racket[markup-source-ext], @racket[markdown-source-ext], @racket[null-source-ext], @racket[pagetree-source-ext], @racket[template-source-ext], @racket[scribble-source-ext], @racket[poly-source-ext], @racket[cache-dir-name], @racket[cache-subdir-name], @racket[template-prefix], @racket[fallback-template-prefix], @racket[template-meta-key], @racket[main-export], @racket[meta-export], @racket[meta-tag-name], @racket[define-meta-name].
@subsection{Version 2.2}
Added @racket[--null] and @racket[--dry-run] switches to @secref["raco_pollen_render" #:doc '(lib "pollen/scribblings/pollen.scrbl")].
Extended the @racket[define-meta] form to allow multiple key–value pairs.
Changed handling of @racket[current-metas] so that values can be updated by tag functions during the evaluation of a source file.
Switched to MIT license.
@subsection{Version 2.1}
Added @racket[setup:envvar-watchlist].
@seclink["raco-pollen" #:doc '(lib "pollen/scribblings/pollen.scrbl")]{@racketfont{raco pollen}}: Introduced support for parallel processing by adding @racket[--parallel] and @racket[--jobs] options to @secref["raco_pollen_setup" #:doc '(lib "pollen/scribblings/pollen.scrbl")] and @secref["raco_pollen_render" #:doc '(lib "pollen/scribblings/pollen.scrbl")].
@subsection{Version 2.0}
Dropped support for Racket versions earlier than 6.3.
(raise-argument-error'ID(format"elements need to be passed to tag function as individual trailing arguments (or, if you want to pass them as a single list, use `(apply ~a ···)` here instead of `(~a ···)`)"'ID'ID)(carelems)))))