@ -75,7 +75,7 @@ As mentioned above, a Pollen source file is not code with text embedded in it, b
@item{@bold{If you can write text, you can program in Pollen.} Really. As you already found out in the @secref["quick-tour"], this is a valid Pollen program:
@item{@bold{If you can write text, you can program in Pollen.} Really. As you already found out in the @secref["quick-tour"], this is a valid Pollen program:
@codeblock{
@codeblock{
#lang pollen
#lang pollen
Hello world: how are you on this fine summer day?
Hello setup: how are you on this fine summer day?
}}
}}
@item{@bold{Commands start with ◊.} A simple rule: if a piece of text starts with @litchar{◊}, it's treated as a command; otherwise it's treated as ordinary text.}
@item{@bold{Commands start with ◊.} A simple rule: if a piece of text starts with @litchar{◊}, it's treated as a command; otherwise it's treated as ordinary text.}
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" world:main-export)] and @code[(format "~a" world: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" setup:default-main-export)] and @code[(format "~a" setup:default-meta-export)] —in a cache so they can be reused.
In each directory of your project, Pollen creates a subdirectory called @filepath{pollen-cache}. 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, you should not read or write to any @filepath{pollen-cache} directory, as the implementation details are subject to change.)
In each directory of your project, Pollen creates a subdirectory called @filepath{pollen-cache}. 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, you should not read or write to any @filepath{pollen-cache} directory, as the implementation details are subject to change.)
@ -21,18 +21,18 @@ If you want to reset all the compile caches, use @exec{@seclink["raco_pollen_res
@section{Disabling the cache}
@section{Disabling the cache}
The compile cache is controlled by the @seclink["world-overrides"]{overridable value} @racket[world:current-compile-cache-active]. Thus, to disable the compile cache, add a @racket[world] submodule to your @filepath{pollen.rkt} like so:
The compile cache is controlled by the @seclink["setup-overrides"]{overridable value} @racket[setup:compile-cache-active]. Thus, to disable the compile cache, add a @racket[setup] submodule to your @filepath{pollen.rkt} like so:
@codeblock|{
@codeblock|{
(module world racket/base
(module setup racket/base
(provide (all-defined-out))
(provide (all-defined-out))
(define compile-cache-active #f))
(define compile-cache-active #f))
}|
}|
Pollen also caches rendered output files, so if you want to disable all caching —thus forcing everything to recompile, every time —you should also disable the render cache by overriding @racket[world:current-render-cache-active]:
Pollen also caches rendered output files, so if you want to disable all caching —thus forcing everything to recompile, every time —you should also disable the render cache by overriding @racket[setup:render-cache-active]:
@codeblock|{
@codeblock|{
(module world racket/base
(module setup racket/base
(provide (all-defined-out))
(provide (all-defined-out))
(define compile-cache-active #f)
(define compile-cache-active #f)
(define render-cache-active #f))
(define render-cache-active #f))
@ -68,7 +68,7 @@ hash-eq?]
)]
)]
Try to retrieve the requested value out of the cache. If it's not there, or out of date, @racket[dynamic-require] is used to update it from the source.
Try to retrieve the requested value out of the cache. If it's not there, or out of date, @racket[dynamic-require] is used to update it from the source.
Despite their names, these functions actually rely on @racket[world:current-main-export] and @racket[world:current-meta-export] (which default to @id[world:main-export] and @id[world:meta-export]). Thus, if you override those names, everything will still work as expected.
Despite their names, these functions actually rely on @racket[setup:main-export] and @racket[setup:meta-export] (which default to @id[setup:default-main-export] and @id[setup: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 @bold{always} use @racket[cached-doc] and @racket[cached-metas] to get data from Pollen source files. That doesn't mean you can't also use functions like @racket[require], @racket[local-require], and @racket[dynamic-require]. They'll just be slower.
If you want the speed benefit of the cache, you should @bold{always} use @racket[cached-doc] and @racket[cached-metas] to get data from Pollen source files. That doesn't mean you can't also use functions like @racket[require], @racket[local-require], and @racket[dynamic-require]. They'll just be slower.
Still, if you don't want to use the lozenge as your command character, you can set Pollen's @racket[world:command-char] value to whatever character you want (see also @seclink["world-overrides"]).
Still, if you don't want to use the lozenge as your command character, you can set Pollen's @racket[setup:default-command-char] value to whatever character you want (see also @seclink["setup-overrides"]).
@margin-note{Scribble uses the @"@" sign as a delimiter. It's not a bad choice if you only work with Racket files. But as you use Pollen to work on other kinds of text-based files that commonly contain @"@" signs — HTML pages especially — it gets cumbersome. So I changed it.}
@margin-note{Scribble uses the @"@" sign as a delimiter. It's not a bad choice if you only work with Racket files. But as you use Pollen to work on other kinds of text-based files that commonly contain @"@" signs — HTML pages especially — it gets cumbersome. So I changed it.}
@ -23,7 +23,7 @@ You can retrieve a meta value —even in the same document where you define it
For an introduction to metas, see @secref["Inserting_metas"].
For an introduction to metas, see @secref["Inserting_metas"].
@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[world:current-splicing-tag].
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].
@examples[#:eval my-eval
@examples[#:eval my-eval
(module splicer pollen/markup
(module splicer pollen/markup
@ -55,9 +55,9 @@ Functions for retrieving data out of Pollen source files. These are not the only
(or/c txexpr? string?)]
(or/c txexpr? string?)]
Retrieve the @racket[doc] export from @racket[_doc-source], which can be either a path, path string, or pagenode that can be resolved into a source path. If @racket[_doc-source] cannot be resolved, raise an error.
Retrieve the @racket[doc] export from @racket[_doc-source], which can be either a path, path string, or pagenode that can be resolved into a source path. If @racket[_doc-source] cannot be resolved, raise an error.
If @racket[_doc-source] is a relative path or pagenode, it is treated as being relative to @racket[world: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]).
If @racket[_doc-source] is a relative path or pagenode, it is treated as being relative to @racket[setup: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]).
If @racket[world:current-main-export] has been overridden with a project-specific value, then that is retrieved instead.
If @racket[setup:main-export] has been overridden with a project-specific value, then that is retrieved instead.
@defproc[
@defproc[
@ -66,9 +66,9 @@ If @racket[world:current-main-export] has been overridden with a project-specifi
hash?]
hash?]
Retrieve the @racket[metas] export from @racket[_meta-source], which can be either a path, path string, or pagenode that can be resolved into a source path. If @racket[_meta-source] cannot be resolved, raise an error.
Retrieve the @racket[metas] export from @racket[_meta-source], which can be either a path, path string, or pagenode that can be resolved into a source path. If @racket[_meta-source] cannot be resolved, raise an error.
If @racket[_meta-source] is a relative path or pagenode, it is treated as being relative to @racket[world: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]).
If @racket[_meta-source] is a relative path or pagenode, it is treated as being relative to @racket[setup: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]).
If @racket[world:current-meta-export] has been overridden with a project-specific value, then that is retrieved instead.
If @racket[setup:meta-export] has been overridden with a project-specific value, then that is retrieved instead.
@deftogether[(
@deftogether[(
@ -92,7 +92,7 @@ With @racket[select], you get the first result; with @racket[select*], you get t
In both cases, you get @racket[#f] if there are no matches.
In both cases, you get @racket[#f] if there are no matches.
Note that if @racket[_value-source] is a relative path or pagenode, it is treated as being relative to @racket[world: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[_value-source] is a relative path or pagenode, it is treated as being relative to @racket[setup: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]).
@examples[#:eval my-eval
@examples[#:eval my-eval
(module nut-butters pollen/markup
(module nut-butters pollen/markup
@ -115,7 +115,7 @@ Note that if @racket[_value-source] is a relative path or pagenode, it is treate
(or/c #f (listof xexpr?))]
(or/c #f (listof xexpr?))]
Look up the value of @racket[_key] in @racket[_doc-source]. The @racket[_doc-source] argument can be either 1)a tagged X-expression representing a @racket[doc] or 2)a pagenode or source path that identifies a source file that provides @racket[doc]. If no value exists for @racket[_key], you get @racket[#f].
Look up the value of @racket[_key] in @racket[_doc-source]. The @racket[_doc-source] argument can be either 1)a tagged X-expression representing a @racket[doc] or 2)a pagenode or source path that identifies a source file that provides @racket[doc]. If no value exists for @racket[_key], you get @racket[#f].
Note that if @racket[_doc-source] is a relative path or pagenode, it is treated as being relative to @racket[world: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[_doc-source] is a relative path or pagenode, it is treated as being relative to @racket[setup: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]).
@examples[#:eval my-eval
@examples[#:eval my-eval
(module gelato pollen/markup
(module gelato pollen/markup
@ -137,7 +137,7 @@ Note that if @racket[_doc-source] is a relative path or pagenode, it is treated
(or/c #f xexpr?)]
(or/c #f xexpr?)]
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[world: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[setup: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]).
@(my-eval `(require pollen pollen/decode xml racket/list txexpr))
@(my-eval `(require pollen pollen/decode xml racket/list txexpr))
@ -241,7 +241,7 @@ Identical to @racket[decode], but takes @racket[txexpr-elements?] as input rathe
(block-txexpr?
(block-txexpr?
[v any/c])
[v any/c])
boolean?]
boolean?]
Predicate that tests whether @racket[_v] has a tag that is among the @racket[world:current-block-tags]. If not, it is treated as inline.
Predicate that tests whether @racket[_v] has a tag that is among the @racket[setup:block-tags]. If not, it is treated as inline.
This predicate affects the behavior of other functions. For instance, @racket[decode-paragraphs] knows that block elements in the markup shouldn't be wrapped in a @racket[p] tag. So if you introduce a new block element called @racket[bloq] without configuring it as a block, misbehavior will follow:
This predicate affects the behavior of other functions. For instance, @racket[decode-paragraphs] knows that block elements in the markup shouldn't be wrapped in a @racket[p] tag. So if you introduce a new block element called @racket[bloq] without configuring it as a block, misbehavior will follow:
@ -251,13 +251,13 @@ This predicate affects the behavior of other functions. For instance, @racket[de
(code:comment @#,t{Wrong: bloq should not be wrapped})
(code:comment @#,t{Wrong: bloq should not be wrapped})
]
]
To change how this test works, use a @racket[world] submodule as described in @secref["world-overrides"]:
To change how this test works, use a @racket[setup] submodule as described in @secref["setup-overrides"]:
Within @racket[_elements], merge sequential newline characters into a single element. The newline string is controlled by @racket[world:current-newline], and defaults to @val[world:newline].
Within @racket[_elements], merge sequential newline characters into a single element. The newline string is controlled by @racket[setup:newline], and defaults to @val[setup:default-newline].
@ -286,7 +286,7 @@ Within @racket[_elements], merge sequential newline characters into a single ele
(listof xexpr?)]
(listof xexpr?)]
Within @racket[_elements], convert occurrences of the linebreak separator to @racket[_linebreaker], but only if the separator does not occur between blocks (see @racket[block-txexpr?]). Why? Because block-level elements automatically display on a new line, so adding @racket[_linebreaker] would be superfluous. In that case, the linebreak separator just disappears.
Within @racket[_elements], convert occurrences of the linebreak separator to @racket[_linebreaker], but only if the separator does not occur between blocks (see @racket[block-txexpr?]). Why? Because block-level elements automatically display on a new line, so adding @racket[_linebreaker] would be superfluous. In that case, the linebreak separator just disappears.
The linebreak separator is controlled by @racket[world:current-linebreak-separator], and defaults to @val[world:linebreak-separator].
The linebreak separator is controlled by @racket[setup:linebreak-separator], and defaults to @val[setup:default-linebreak-separator].
The @racket[_linebreaker] argument can either be an X-expression, or a function that takes two X-expressions and returns one. This function will receive the previous and next elements, to make contextual substitution possible.
The @racket[_linebreaker] argument can either be an X-expression, or a function that takes two X-expressions and returns one. This function will receive the previous and next elements, to make contextual substitution possible.
@ -308,7 +308,7 @@ Find paragraphs within @racket[_elements] and wrap them with @racket[_paragraph-
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).
The paragraph separator is controlled by @racket[world:current-paragraph-separator], and defaults to @val[world:paragraph-separator].
The paragraph separator is controlled by @racket[setup:paragraph-separator], and defaults to @val[setup:default-paragraph-separator].
@ -15,22 +15,22 @@ Pollen handles six kinds of source files:
@itemlist[
@itemlist[
@item{@bold{Preprocessor}, with file extension @ext[world:preproc-source-ext]}
@item{@bold{Preprocessor}, with file extension @ext[setup:default-preproc-source-ext]}
@item{@bold{Markup}, with file extension @ext[world:markup-source-ext]}
@item{@bold{Markup}, with file extension @ext[setup:default-markup-source-ext]}
@item{@bold{Markdown}, with file extension @ext[world:markdown-source-ext]}
@item{@bold{Markdown}, with file extension @ext[setup:default-markdown-source-ext]}
@item{@bold{Null}, with file extension @ext[world:null-source-ext]}
@item{@bold{Null}, with file extension @ext[setup:default-null-source-ext]}
@item{@bold{Scribble}, with file extension @ext[world:scribble-source-ext]}
@item{@bold{Scribble}, with file extension @ext[setup:default-scribble-source-ext]}
@item{@bold{Pagetree}, with file extension @ext[world:pagetree-source-ext]. This is the only source type that does not produce an output file.}
@item{@bold{Pagetree}, with file extension @ext[setup:default-pagetree-source-ext]. This is the only source type that does not produce an output file.}
]
]
The functions in this module rely on file extensions specified in @racketmodname[pollen/world]. These extensions can be overridden within a project — see @secref["world-overrides"].
The functions in this module rely on file extensions specified in @racketmodname[pollen/setup]. These extensions can be overridden within a project — see @secref["setup-overrides"].
For each kind of Pollen source file, the corresponding output file name is derived by removing the extension from the name of the source file. So the preprocessor source file @filepath{default.css.pp} would become @filepath{default.css}. (See
For each kind of Pollen source file, the corresponding output file name is derived by removing the extension from the name of the source file. So the preprocessor source file @filepath{default.css.pp} would become @filepath{default.css}. (See
@secref["Saving___naming_your_source_file"] if this rings no bells.)
@secref["Saving___naming_your_source_file"] if this rings no bells.)
@ -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[world:poly-source-ext] output type}, then @racket[->output-path] uses @racket[world:current-poly-target] as the output-path extension.
If @racket[_p] has a @seclink["The_poly_output_type"]{@id[setup:default-poly-source-ext] output type}, then @racket[->output-path] uses @racket[setup: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}.
@ -46,7 +46,7 @@ The second export, @racket[metas], is a hashtable of key–value pairs with extr
Pollen source files also make the @racket[metas] hashtable available through a submodule, also called @racket[metas]. So rather than importing a source file with @racket[(require "source.html.pm")], you would @racket[(require (submod "source.html.pm" metas))]. Accessing the metas this way avoids fully compiling the source file, and thus will usually be faster.
Pollen source files also make the @racket[metas] hashtable available through a submodule, also called @racket[metas]. So rather than importing a source file with @racket[(require "source.html.pm")], you would @racket[(require (submod "source.html.pm" metas))]. Accessing the metas this way avoids fully compiling the source file, and thus will usually be faster.
The names @racket[doc] and @racket[metas] can be changed for a project by overriding @racket[world:main-export] and @racket[world:meta-export].
The names @racket[doc] and @racket[metas] can be changed for a project by overriding @racket[setup:default-main-export] and @racket[setup:default-meta-export].
@margin-note{The Pollen rendering system relies on these two identifiers, but otherwise doesn't care how they're generated. Meaning, the code inside your Pollen source file could be @tt{#langracket} or @tt{#langwhatever}. As long as you manually @racket[provide] those two identifiers and follow the usual file-naming convention, your source file will be usable.}
@margin-note{The Pollen rendering system relies on these two identifiers, but otherwise doesn't care how they're generated. Meaning, the code inside your Pollen source file could be @tt{#langracket} or @tt{#langwhatever}. As long as you manually @racket[provide] those two identifiers and follow the usual file-naming convention, your source file will be usable.}
@ -65,9 +65,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" world: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" setup:default-preproc-source-ext)}. These forms are equivalent:
@racketmod[#:file "sample.css.pp" pollen
@racketmod[#:file "sample.css.pp" pollen
@ -90,9 +90,9 @@ The output of the preprocessor dialect, provided by @racket['doc], is plain text
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" world: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" setup:default-markdown-source-ext)}. These forms are equivalent:
@racketmod[#:file "sample.txt.pmd" pollen
@racketmod[#:file "sample.txt.pmd" pollen
@ -106,9 +106,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" world: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" setup:default-markup-source-ext)}. These forms are equivalent:
@racketmod[#:file "about.html.pm" pollen
@racketmod[#:file "about.html.pm" pollen
@ -121,10 +121,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" world: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" setup:default-pagetree-source-ext)}. These forms are equivalent:
@racketmod[#:file "main.ptree" pollen
@racketmod[#:file "main.ptree" pollen
@ -148,12 +148,12 @@ 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}.
@ -165,8 +165,8 @@ This can be useful you're managing your project with git. Most likely you'll wan
Pollen relies extensively on the convention of naming source files by adding a source extension to an output-file name. So the Pollen markup source for @filepath{index.html} would be @filepath{index.html.pm}.
Pollen relies extensively on the convention of naming source files by adding a source extension to an output-file name. So the Pollen markup source for @filepath{index.html} would be @filepath{index.html.pm}.
This convention occasionally flummoxes other programs that assume a file can only have one extension. If you run into such a situation, you can @italic{escape} the output-file extension using the @racket[world:extension-escape-char], which defaults to the underscore @litchar{_}.
This convention occasionally flummoxes other programs that assume a file can only have one extension. If you run into such a situation, you can @italic{escape} the output-file extension using the @racket[setup:default-extension-escape-char], which defaults to the underscore @litchar{_}.
So instead of @filepath{index.html.pm}, your source-file name would be @filepath{index_html.pm}. When this source file is rendered, it will automatically be converted into @filepath{index.html} (meaning, the escaped extension will be converted into a normal file extension).
So instead of @filepath{index.html.pm}, your source-file name would be @filepath{index_html.pm}. When this source file is rendered, it will automatically be converted into @filepath{index.html} (meaning, the escaped extension will be converted into a normal file extension).
This alternative-naming scheme is automatically enabled in every project. You can also set the escape character on a per-project basis (see @racket[world:current-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 (see @racket[setup:extension-escape-char]). Pollen will let you choose any character, but of course it would be unwise to pick one with special meaning in your filesystem (for instance, @litchar{/}).
@ -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" world: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" setup:default-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.
@ -422,7 +422,7 @@ Return the pagenode immediately after @racket[_p]. For @racket[next*], return al
[pagetree-source pathish?])
[pagetree-source pathish?])
pagetree?
pagetree?
]
]
Get a pagetree from a @ext[world:pagetree-source-ext] source file, namely @racket[_pagetree-source].
Get a pagetree from a @ext[setup:default-pagetree-source-ext] source file, namely @racket[_pagetree-source].
@defproc[
@defproc[
@ -456,7 +456,7 @@ Report whether @racket[_pagenode] is in @racket[_pagetree].
Convert path @racket[_p] to a pagenode —meaning, make it relative to @racket[_starting-path], run it through @racket[->output-path], and convert it to a symbol. Does not tell you whether the resulting pagenode actually exists in the current pagetree (for that, use @racket[in-pagetree?]).
Convert path @racket[_p] to a pagenode —meaning, make it relative to @racket[_starting-path], run it through @racket[->output-path], and convert it to a symbol. Does not tell you whether the resulting pagenode actually exists in the current pagetree (for that, use @racket[in-pagetree?]).
@ -47,7 +47,7 @@ Displays a list of available commands.
@section{@exec{raco pollen start}}
@section{@exec{raco pollen start}}
Start the project server from the current directory using the default port, which is the value of the parameter @racket[world:current-server-port] (by default, port @(format "~a" world:default-port)).
Start the project server from the current directory using the default port, which is the value of the parameter @racket[setup:current-server-port] (by default, port @(format "~a" setup:default-default-port)).
This command can be invoked with two optional arguments.
This command can be invoked with two optional arguments.
@ -56,7 +56,7 @@ This command can be invoked with two optional arguments.
@terminal{
@terminal{
> raco pollen start ~/path/to/project/}
> raco pollen start ~/path/to/project/}
@racket[raco pollen start _path _port] will start the project server in @racket[_path] using @racket[_port] rather than @racket[world:current-server-port]. This is useful if you want to have multiple project servers running simultaneously.
@racket[raco pollen start _path _port] will start the project server in @racket[_path] using @racket[_port] rather than @racket[setup:current-server-port]. This is useful if you want to have multiple project servers running simultaneously.
@terminal{
@terminal{
> raco pollen start ~/path/to/project/
> raco pollen start ~/path/to/project/
@ -88,7 +88,7 @@ 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[(world:current-poly-targets)].
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)].
@terminal{
@terminal{
> raco pollen render -t pdf foo.poly.pm}
> raco pollen render -t pdf foo.poly.pm}
@ -107,7 +107,7 @@ Make a copy of the project directory on the desktop, but without any source file
If you're already in your project directory and want to publish somewhere other than the desktop, use @racket[raco pollen publish _. _dest-dir].
If you're already in your project directory and want to publish somewhere other than the desktop, use @racket[raco pollen publish _. _dest-dir].
You can determine the files that get filtered out in a particular project by using @racket[world:current-unpublished-path?].
You can determine the files that get filtered out in a particular project by using @racket[setup:unpublished-path?].
@ -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" world: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" setup:default-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]" world:default-template-prefix)]. Meaning, if @racket[_source-path] is @code[(format "intro.html.~a" world:markup-source-ext)], the output path would be @code["intro.html"], so the default template would be @code[(format "~a.html" world: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]" setup:default-default-template-prefix)]. Meaning, if @racket[_source-path] is @code[(format "intro.html.~a" setup:default-markup-source-ext)], the output path would be @code["intro.html"], so the default template would be @code[(format "~a.html" setup:default-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 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"
Global values that are used throughout the Pollen system.
Global values that are used throughout the Pollen system.
@ -15,36 +15,36 @@ Global values that are used throughout the Pollen system.
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].
@defparam[world:current-server-port port integer?]{
@defparam[setup:current-server-port port integer?]{
A parameter that sets the HTTP port for the project server. Initialized to @racket[world:default-port].}
A parameter that sets the HTTP port for the project server. Initialized to @racket[setup:default-default-port].}
@defparam[world:current-project-root port path?]{
@defparam[setup:current-project-root port 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}).}
@defparam[world:current-server-extras-path dir path?]{
@defparam[setup:current-server-extras-path dir path?]{
A parameter that reports the path to the directory of support files for the project server. Initialized to @racket[#f], but set to a proper value when the server runs.}
A parameter that reports the path to the directory of support files for the project server. Initialized to @racket[#f], but set to a proper value when the server runs.}
These values can be changed by overriding them in your @racket["pollen.rkt"] source file:
These values can be changed by overriding them in your @racket["pollen.rkt"] source file:
@itemlist[#:style 'ordered
@itemlist[#:style 'ordered
@item{Within this file, @seclink["submodules" #:doc '(lib "scribblings/guide/guide.scrbl")]{create a submodule} called @racket[world].}
@item{Within this file, @seclink["submodules" #:doc '(lib "scribblings/guide/guide.scrbl")]{create a submodule} called @racket[setup].}
@item{Within this submodule, use @racket[define] to make a variable with the same name as the one in @racket[pollen/world], but without the @racket[world:] prefix.}
@item{Within this submodule, use @racket[define] to make a variable with the same name as the one in @racket[pollen/setup], but without the @racket[setup:] prefix.}
@item{Assign it whatever value you like.}
@item{Assign it whatever value you like.}
@item{Repeat as needed.}
@item{Repeat as needed.}
]
]
When Pollen runs, these definitions will supersede those in @racket[pollen/world].
When Pollen runs, these definitions will supersede those in @racket[pollen/setup].
For instance, suppose you wanted the main export of every Pollen source file to be called @racket[van-halen] rather than @racket[doc], the extension of Pollen markup files to be @racket[.rock] rather than @racket[.pm], and the command character to be @litchar{🎸} instead of @litchar{◊}. Your @racket["pollen.rkt"] would look like this:
For instance, suppose you wanted the main export of every Pollen source file to be called @racket[van-halen] rather than @racket[doc], the extension of Pollen markup files to be @racket[.rock] rather than @racket[.pm], and the command character to be @litchar{🎸} instead of @litchar{◊}. Your @racket["pollen.rkt"] would look like this:
@ -54,18 +54,18 @@ For instance, suppose you wanted the main export of every Pollen source file to
;; ... the usual definitions and tag functions ...
;; ... the usual definitions and tag functions ...
(module world racket/base
(module setup racket/base
(provide (all-defined-out))
(provide (all-defined-out))
(define main-export 'van-halen)
(define main-export 'van-halen)
(define markup-source-ext 'rock)
(define markup-source-ext 'rock)
(define command-char #\🎸))
(define command-char #\🎸))
}]
}]
Though any of the values below can be overridden, it may not always be wise to do so. For instance, if you redefined @racket[world:fallback-template-prefix], you would simply break the fallback-template mechanism, because it would look for files that don't exist. But we don't live in a nanny state, so you are entrusted to say what you mean and accept the consequences.
Though any of the values below can be overridden, it may not always be wise to do so. For instance, if you redefined @racket[setup:default-fallback-template-prefix], you would simply break the fallback-template mechanism, because it would look for files that don't exist. But we don't live in a nanny state, so you are entrusted to say what you mean and accept the consequences.
Of course, you can restore the defaults simply by removing these defined values from @racket["pollen.rkt"].
Of course, you can restore the defaults simply by removing these defined values from @racket["pollen.rkt"].
These values are each equipped with a corresponding @racket[world:current-]@racket[_name] function that will return the value loaded from the @racket[world] submodule (if @racket[_name] was defined there), otherwise it returns the original value for @racket[world:]@racket[_name]. For instance, @racket[world:command-char] will always be @litchar{◊}, but in the example above, @racket[world:current-command-char] would return@litchar{🎸}.
These values are each equipped with a corresponding @racket[setup:]@racket[_name] function that will return the value loaded from the @racket[setup] submodule (if @racket[_name] was defined there), otherwise it returns the original value for @racket[setup:]@racket[_name]. For instance, @racket[setup:default-command-char] will always be @litchar{◊}, but in the example above, @racket[setup:command-char] would return@litchar{🎸}.
@defoverridable[default-port integer?]{
@defoverridable[default-port integer?]{
@ -94,13 +94,13 @@ Determines the default HTTP port for the project server. Initialized to @racket[
)]
)]
File extensions for Pollen source files, initialized to the following values:
File extensions for Pollen source files, initialized to the following values:
@defoverridable[decodable-extensions (listof symbol?)]{File extensions that are eligible for decoding.}
@defoverridable[decodable-extensions (listof symbol?)]{File extensions that are eligible for decoding.}
@ -116,9 +116,9 @@ File extensions for Pollen source files, initialized to the following values:
@defoverridable[block-tags (listof symbol?)]{Tags that are treated as blocks by @racket[block-txexpr?]. Initialized to the @link["https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements"]{block-level elements in HTML5}, namely:
@defoverridable[block-tags (listof symbol?)]{Tags that are treated as blocks by @racket[block-txexpr?]. Initialized to the @link["https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements"]{block-level elements in HTML5}, namely:
@ -153,7 +153,7 @@ Default separators used in decoding. The first two are initialized to @racket["\
@defoverridable[here-path-key symbol?]{Key used to store the absolute path of the current source file in its @racket[metas] hashtable. Default is @racket['here-path].}
@defoverridable[here-path-key symbol?]{Key used to store the absolute path of the current source file in its @racket[metas] hashtable. Default is @racket['here-path].}
@defoverridable[splicing-tag symbol?]{Key used to signal that an X-expression should be spliced into its containing X-expression. Default is @val[world:splicing-tag].}
@defoverridable[splicing-tag symbol?]{Key used to signal that an X-expression should be spliced into its containing X-expression. Default is @val[setup:default-splicing-tag].}
@defoverridable[poly-source-ext symbol?]{Extension that indicates a source file can target multiple output types. Default is @racket['poly].}
@defoverridable[poly-source-ext symbol?]{Extension that indicates a source file can target multiple output types. Default is @racket['poly].}
@title[#:tag "first-tutorial"]{First tutorial: the project server & preprocessor}
@title[#:tag "first-tutorial"]{First tutorial: the project server & preprocessor}
@ -234,7 +234,7 @@ Though port @tt{8080} is the default, you can start the project server on any po
> raco pollen start /path/to/tutorial 8088
> raco pollen start /path/to/tutorial 8088
}
}
@margin-note{You can also change the default port by altering @racket[world:default-port], or parameterizing it with @racket[world:current-server-port].}
@margin-note{You can also change the default port by altering @racket[setup:default-default-port], or parameterizing it with @racket[setup:current-server-port].}
Note that when you pass a port argument, you also have to pass a path argument. (Without it, you'll get an error, as illustrated below.) If you want the project server to start in the current directory, you can use the usual @litchar{.} shorthand:
Note that when you pass a port argument, you also have to pass a path argument. (Without it, you'll get an error, as illustrated below.) If you want the project server to start in the current directory, you can use the usual @litchar{.} shorthand:
@ -16,7 +16,7 @@ In previous tutorial projects, we've maintained a one-to-one relationship betwee
@item{Setting up & using @tt{poly} source files}
@item{Setting up & using @tt{poly} source files}
@item{The @tt{world} submodule}
@item{The @tt{setup} submodule}
@item{Branching tag functions}
@item{Branching tag functions}
@ -83,7 +83,7 @@ In the previous tutorials, you saw how Pollen source files correspond to certain
In a multiple-output project, a source file no longer has a one-to-one correspondence with a specific output type. To indicate this, we'll instead use the special @tt{poly} extension. So our @filepath{document.html.pm} will become @filepath{document.poly.pm}.
In a multiple-output project, a source file no longer has a one-to-one correspondence with a specific output type. To indicate this, we'll instead use the special @tt{poly} extension. So our @filepath{document.html.pm} will become @filepath{document.poly.pm}.
@margin-note{The @tt{poly} extension is the default, but can be changed for a project by using the @racket[world:current-poly-source-ext] setting.}
@margin-note{The @tt{poly} extension is the default, but can be changed for a project by using the @racket[setup:poly-source-ext] setting.}
Let's set up a new multi-output project for a résumé. Find a convenient directory and create a new @tt{poly} source file as follows:
Let's set up a new multi-output project for a résumé. Find a convenient directory and create a new @tt{poly} source file as follows:
@ -142,11 +142,11 @@ Today is @(date->string (current-date)). I @bold{really} want this job.
Though Pollen imputes HTML as a target for poly sources by default, if you only wanted HTML, you wouldn't be using a poly source. So our next step will be to explicitly define the output targets that we want to associate with poly sources.
Though Pollen imputes HTML as a target for poly sources by default, if you only wanted HTML, you wouldn't be using a poly source. So our next step will be to explicitly define the output targets that we want to associate with poly sources.
@subsubsection{Using the @tt{world} submodule}
@subsubsection{Using the @tt{setup} submodule}
We'll do this by setting the @racket[world:current-poly-targets] value in our @filepath{pollen.rkt}. If you haven't investigated it yet, the @racket[pollen/world] module offers @seclink["world-overrides"] that allow you to configure certain Pollen characteristics from within a @filepath{pollen.rkt} file. The example on that page, for instance, shows how to change the markup source extension and the Pollen command character.
We'll do this by setting the @racket[setup:poly-targets] value in our @filepath{pollen.rkt}. If you haven't investigated it yet, the @racket[pollen/setup] module offers @seclink["setup-overrides"] that allow you to configure certain Pollen characteristics from within a @filepath{pollen.rkt} file. The example on that page, for instance, shows how to change the markup source extension and the Pollen command character.
The idea is that you add a @racket[world] submodule to your @filepath{pollen.rkt} file with a @racket[define] statement for the value. Because we're defining the local value, we drop the @racket[world:current-] prefix and just call it @racket[poly-targets]. Our value will be a list of file extensions denoting the targets. To start, let's set our output formats to HTML and plain text, which we'll denote with the list of extensions @racket['(html txt)].
The idea is that you add a @racket[setup] submodule to your @filepath{pollen.rkt} file with a @racket[define] statement for the value. Because we're defining the local value, we drop the @racket[setup:] prefix and just call it @racket[poly-targets]. Our value will be a list of file extensions denoting the targets. To start, let's set our output formats to HTML and plain text, which we'll denote with the list of extensions @racket['(html txt)].
@margin-note{I'm glossing over the details of @seclink["submodules" #:doc '(lib "scribblings/guide/guide.scrbl")], but they're one of the best-considered features of the Racket language. What makes submodules so handy is that they are truly independent: you can load a submodule from a source file without running the main body of the file. Thus, tasks like this —setting configuration values — that might require separate files in other languages can be handled as submodules in Racket.}
@margin-note{I'm glossing over the details of @seclink["submodules" #:doc '(lib "scribblings/guide/guide.scrbl")], but they're one of the best-considered features of the Racket language. What makes submodules so handy is that they are truly independent: you can load a submodule from a source file without running the main body of the file. Thus, tasks like this —setting configuration values — that might require separate files in other languages can be handled as submodules in Racket.}
@ -155,7 +155,7 @@ The idea is that you add a @racket[world] submodule to your @filepath{pollen.rkt
(require racket/date)
(require racket/date)
(provide (all-defined-out))
(provide (all-defined-out))
(module config racket/base
(module setup racket/base
(provide (all-defined-out))
(provide (all-defined-out))
(define poly-targets '(html txt)))
(define poly-targets '(html txt)))
@ -173,7 +173,7 @@ Though you ordinarily don't have to restart the project server to see changes in
@image/rp["poly-ps-html-txt.png" #:scale 0.45]
@image/rp["poly-ps-html-txt.png" #:scale 0.45]
What's happened is that @racket[world:current-poly-targets] now reflects the settings in @filepath{pollen.rkt}. The project server sees that we want to associate poly files with HTML and plain-text targets, and accordingly shows us two entries in the project-server listing: @filepath{cv.html.pm} and @filepath{cv.txt.pm}. As the adjacent message indicates, these are not new source files on disk, but rather implied by @filepath{cv.poly.pm}.
What's happened is that @racket[setup:poly-targets] now reflects the settings in @filepath{pollen.rkt}. The project server sees that we want to associate poly files with HTML and plain-text targets, and accordingly shows us two entries in the project-server listing: @filepath{cv.html.pm} and @filepath{cv.txt.pm}. As the adjacent message indicates, these are not new source files on disk, but rather implied by @filepath{cv.poly.pm}.
If you click on @filepath{cv.html.pm}, you'll see the same HTML output that you saw before. If you click on @filepath{cv.txt.pm}, however, you'll see this:
If you click on @filepath{cv.html.pm}, you'll see the same HTML output that you saw before. If you click on @filepath{cv.txt.pm}, however, you'll see this:
@ -221,14 +221,14 @@ But plain text doesn't have @racket[h2] or @racket[strong]. So how about this: w
``So how do we make our tags mean one thing for HTML and a different thing for plain text?'' We make @italic{branching tag functions} that do different things depending on what the current rendering target for poly sources is.
``So how do we make our tags mean one thing for HTML and a different thing for plain text?'' We make @italic{branching tag functions} that do different things depending on what the current rendering target for poly sources is.
That value, in fact, is stored in a Pollen @seclink["parameterize" #:doc '(lib "scribblings/guide/guide.scrbl")]{parameter} called @racket[(world:current-poly-target)]. What we're going to do is rewrite our tag functions to behave differently based on the value of this parameter. Update your @filepath{pollen.rkt} as follows:
That value, in fact, is stored in a Pollen @seclink["parameterize" #:doc '(lib "scribblings/guide/guide.scrbl")]{parameter} called @racket[(setup:current-poly-target)]. What we're going to do is rewrite our tag functions to behave differently based on the value of this parameter. Update your @filepath{pollen.rkt} as follows:
@fileblock["pollen.rkt" @codeblock|{
@fileblock["pollen.rkt" @codeblock|{
#lang racket/base
#lang racket/base
(require racket/date pollen/world)
(require racket/date pollen/setup)
(provide (all-defined-out))
(provide (all-defined-out))
(module config racket/base
(module setup racket/base
(provide (all-defined-out))
(provide (all-defined-out))
(define poly-targets '(html txt)))
(define poly-targets '(html txt)))
@ -236,12 +236,12 @@ That value, in fact, is stored in a Pollen @seclink["parameterize" #:doc '(lib "
(date->string (current-date)))
(date->string (current-date)))
(define (heading . xs)
(define (heading . xs)
(case (world:current-poly-target)
(case (setup:current-poly-target)
[(txt) (map string-upcase xs)]
[(txt) (map string-upcase xs)]
[else `(h2 ,@xs)]))
[else `(h2 ,@xs)]))
(define (emph . xs)
(define (emph . xs)
(case (world:current-poly-target)
(case (setup:current-poly-target)
[(txt) `("**" ,@xs "**")]
[(txt) `("**" ,@xs "**")]
[else `(strong ,@xs)]))
[else `(strong ,@xs)]))
}|]
}|]
@ -269,10 +269,10 @@ Let's see how fast we can add support for LaTeX output. Here's the updated @file
@fileblock["pollen.rkt" @codeblock|{
@fileblock["pollen.rkt" @codeblock|{
#lang racket/base
#lang racket/base
(require racket/date pollen/world)
(require racket/date pollen/setup)
(provide (all-defined-out))
(provide (all-defined-out))
(module config racket/base
(module setup racket/base
(provide (all-defined-out))
(provide (all-defined-out))
(define poly-targets '(html txt ltx)))
(define poly-targets '(html txt ltx)))
@ -280,13 +280,13 @@ Let's see how fast we can add support for LaTeX output. Here's the updated @file
S-expressions use prefix notation, where each pair of parentheses contains a list. The first element in the list names a function, and the other elements are the arguments to that function. (This is a review of @secref["Racket_basics__if_you_re_not_familiar_"].) X-expressions are just a minor adaptation of S-expression notation to represent markup, hence the name (the @defterm{X} is short for @defterm{XML-like}).
S-expressions use prefix notation, where each pair of parentheses contains a list. The first element in the list names a function, and the other elements are the arguments to that function. (This is a review of @secref["Racket_basics__if_you_re_not_familiar_"].) X-expressions are just a minor adaptation of S-expression notation to represent markup, hence the name (the @defterm{X} is short for @defterm{XML-like}).
@ -336,7 +336,7 @@ But within a template, we need to tell Pollen how we want to convert the X-expre
Third, we need to include the content from our source file. By convention, every Pollen source file makes its output available through an exported variable named @code{doc}. A source file in preprocessor mode puts its text result in @code{doc}. And a source file in authoring mode puts its X-expression result in @code{doc}. So we put the variable @code{doc} inside the @code{body} tag.
Third, we need to include the content from our source file. By convention, every Pollen source file makes its output available through an exported variable named @code{doc}. A source file in preprocessor mode puts its text result in @code{doc}. And a source file in authoring mode puts its X-expression result in @code{doc}. So we put the variable @code{doc} inside the @code{body} tag.
@margin-note{You can change the name to something other than @code{doc} by changing @racket[world:main-export].}
@margin-note{You can change the name to something other than @code{doc} by changing @racket[setup:default-main-export].}