Index: code-docs/cache.scrbl ================================================================== --- code-docs/cache.scrbl +++ code-docs/cache.scrbl @@ -217,7 +217,16 @@ [title string/f] [published string/f] [noun-plural string/f] [noun-singular string/f]) #:constructor-name make-cache:series]{ -Table holding cached-information on series. +Table holding cached information on series. +} + +@defstruct*[cache:index-entry ([id id/f] + [entry string/f] + [subentry string/f] + [page symbol/f] + [html-anchor string/f]) + #:constructor-name make-cache:index-entry]{ +Table holding cached information about index entries found in articles. } Index: code-docs/crystalize.scrbl ================================================================== --- code-docs/crystalize.scrbl +++ code-docs/crystalize.scrbl @@ -4,35 +4,46 @@ @; This file is licensed under the Blue Oak Model License 1.0.0. @(require "scribble-helpers.rkt") @(require (for-label "../pollen.rkt" - "../dust.rkt" "../crystalize.rkt" + "../cache.rkt" racket/base racket/contract racket/string txexpr pollen/pagetree)) -@title{@filepath{crystalize.rkt}} +@title[#:tag "crystalize-rkt"]{Crystalize} @defmodule["crystalize.rkt" #:packages ()] -“Crystalizing” is an extra layer in between docs and templates that destructures the doc and stores -it in various pieces in a SQLite cache. Individual articles save chunks of rendered HTML to the -cache when their individual pages are rendered. When pulling together listings of articles in +“Crystalizing” is an extra layer in between docs and templates that destructures the @tt{doc} and +stores it in various pieces in a SQLite cache. Individual articles save chunks of rendered HTML to +the cache when their individual pages are rendered. When pulling together listings of articles in different contexts that need to be filtered and sorted, a SQL query is much faster than trolling through the Pollen cache for matching docs and regenerating the HTML. -@defproc[(parse-and-cache-article! [pagenode pagenode?] [doc txexpr?]) non-empty-string?] - -Returns a string containing the HTML of @racket[_doc]. @margin-note{This is one function that breaks -my convention of using a prefix of @tt{html$-} for functions that return a single string of HTML.} - -Privately, it does a lot of other work. The article is analyzed, additional metadata is constructed, -and it is saved to the SQLite cache. If the article specifies a @racket['series] meta, information -about that series is fetched and used in the rendering of the article. If there are @racket[note]s -in the doc, they are parsed and saved individually to the SQLite cache. If any of the notes use the -@code{#:disposition} attribute, information about the disposition is parsed out and used in the -rendering of the article. - +@margin-note{These functions are designed to be used within the template for articles and series, +respectively, so that the cache is updated precisely when the web page is rendered.} + +@defproc[(parse-and-cache-article! [pagenode pagenode?] [doc txexpr?]) non-empty-string?]{ + +Returns a string containing the HTML of @racket[_doc]. + +Privately, it does a lot of other work. The article is analyzed, additional metadata is constructed +and saved to the SQLite cache and saved using @racket[make-cache:article]. If the article specifies +a @racket['series] meta, information about that series is fetched and used in the rendering of the +article. If there are @racket[note]s or @racket[index] tags in the doc, they are parsed and saved +individually to the SQLite cache (using @racket[make-cache:note] and @racket[make-cache:index-entry] +respectively). If any of the notes use the @code{#:disposition} attribute, information about the +disposition is parsed out and used in the rendering of the article. +} + +@defproc[(cache-series!) void?]{ + +Attempts to look up certain values in @racket[current-metas] which we expect to be defined on +a typical series page, and saves them to the cache using @racket[make-cache:series]. If @tt{title} +is not defined in the current metas, you’ll get an error. If any of the others are missing, an empty +string is used. +} Index: code-docs/design.scrbl ================================================================== --- code-docs/design.scrbl +++ code-docs/design.scrbl @@ -2,40 +2,156 @@ @; SPDX-License-Identifier: BlueOak-1.0.0 @; This file is licensed under the Blue Oak Model License 1.0.0. @(require "scribble-helpers.rkt" - racket/runtime-path) + (for-label "../pollen.rkt")) @(require (for-label racket/base)) -@title{Design Background} +@title{Basic Notions} + +@section[#:tag "design-goals"]{Design Goals} -The design and implementation of @italic{The Local Yarn} are guided by requirements that have -evolved since I started the site in 1999. I enumerate them here because they explain why the code is -necessarily more complicated than a typical blog. +The design of @italic{The Local Yarn} is guided by requirements that have evolved since I started +the site in 1999. I enumerate them here because they explain why the code is necessarily more +complicated than a typical blog: @itemlist[ - @item{@bold{The writing will publish to two places from the same source: the web server, and the - bookshelf.} The web server, because it’s a fun, fast way to publish writing and code to the whole - world (you knew that already); but also on bookshelves, because - @ext-link["https://thelocalyarn.com/excursus/secretary/posts/web-books.html"]{a web server is like - a projector}, and I want to be able to turn it off someday and still have something to show for - all my work. Plus, I just like printed books.} - - @item{@bold{Changes are part of the content.} I like to revisit, resurface and amend things I’ve - written before. Views change, new ideas come along. In a typical blog the focus is always at - whatever’s happening at the head of the time stream; an addendum to an older post is, for all - practical purposes, invisible and nearly useless. I want every published edit to an article to be - findable and linkable. I want addenda to be extremely visible. These addenda should also be able - to mark major shifts in the author’s own perspective on what they originally wrote.} - - @item{@bold{Everything produced here, both in print and on the web, should look good.} - @item{@bold{All the output should be produced (and reproducible) by automated processes.} No - clicking on buttons in apps to publish web pages or books.} -] - -@nested[#:style 'inset]{ - “Yet modest ornament with use combined @(linebreak) - Attracts the eye to exercise the mind.” @(linebreak) - —@ext-link["https://en.wikipedia.org/wiki/Samuel_Rogers"]{Samuel Rogers} -} + @item{@bold{The writing will publish to two places from the same source: the web server, and the + bookshelf.} The web server, because it’s a fun, fast way to publish writing and code to the whole + world (you knew that already); but also on bookshelves, because + @ext-link["https://thelocalyarn.com/excursus/secretary/posts/web-books.html"]{a web server is like + a projector}, and I want to be able to turn it off someday and still have something to show for all + my work. Plus, I just like printed books.} + + @item{@bold{Changes are part of the content.} I like to revisit, resurface and amend things I’ve + written before. Views change, new ideas come along. In a typical blog the focus is always at + whatever’s happening at the head of the time stream; an addendum to an older post is, for all + practical purposes, invisible and nearly useless. I want every published edit to an article to be + findable and linkable. I want addenda to be extremely visible. These addenda should also be able to + mark major shifts in the author’s own perspective on what they originally wrote.} + + @item{@bold{Experimentation must be accomodated gracefully.} I should be able to write fiction, + poetry, opinion pieces, minor observations or collections, or anything else, and have or create + a good home for it here. Where dissimilar writings appear together, there should be signals that + help the reader understand what they are looking at, switch contexts, and find more if they wish.} + + @item{@bold{Everything produced here should look good.}} + + @item{@bold{Reward exploration without disorienting the reader.}} + + @item{@bold{Everything produced here should be the result of an automatable process.} No clicking + around to publish web pages and books.} + + ] + +@section{Names for things and how they fit together} + +The Local Yarn is mostly comprised of @tech{articles} (individual writings) which may contain +@tech{notes} (addenda by the author or others) and may also be grouped into @tech{series}. These are +similar to a typical blog’s @italic{posts}, @italic{comments} and @italic{categories}, but there are +important differences. + +@subsection{Articles} + +The @deftech{article} is the basic unit of content, like a typical blog post. In the web edition, +each article has its own @tt{.html} file; in print editions, an article may comprise either +a chapter or a part of a chapter, depending on the content. + +An article can start out very small — just a date and a few sentences. Supplying a title is +optional. Later, it may grow in any of several directions: @tech{notes} can be added, or a title, or +cross-references to later articles; or it may be added to a series. Or it may just remain the way it +started. + +@subsection{Notes} + +A @deftech{note} is a comment or addendum to an @tech{article} using the @racket[note] tag. It may +be written by the same person who wrote the article, or submitted by a reader. + +A note appears at the bottom of the article to which it is attached, but it also appears in the blog +and in the RSS feed as a separate piece of content, and is given the same visual weight as actual +articles. + +A note may optionally have a @deftech{disposition} which reflects a change in attitude towards its +parent article. A disposition consists of a @italic{disposition mark} such as an asterisk or dagger, +and a past-tense verb. For example, an author may revisit an opinion piece written years earlier and +add a note describing how their opinion has changed; the tag for this note might include +@racket[#:disposition "* recanted"] as an attribute. This would cause the @tt{*} to be added to the +article’s title, and the phrase “Now considered recanted” to be added to the margin, with a link to +the note. + +@subsubsection{Notes vs. blog “comments”} + +Typical blog comments serve as kind of a temporary discussion spot for a few days or weeks after +a post is published. Commenting on an old post feels useless because the comment is only visible at +the bottom of its parent post, and older posts are never “bumped” back into visibility. + +By contrast, notes on @italic{The Local Yarn} appear as self-contained writings at the top of the +blog and RSS feed as soon as they are published. This “resurfaces” the original article +to which they are attached. This extra visibility also makes them a good tool for the original +author to fill out or update the article. In effect, with notes, each article potentially becomes +its own miniature blog. + +The flip side of this change is that what used to be the “comment section” is no longer allowed to +function as a kind of per-article chat. + +@tabular[#:sep @hspace[1] + #:style 'boxed + #:row-properties '((bottom-border top)) + (list + (list @bold{Typical Blog Comments} @bold{Local Yarn @emph{Notes}}) + (list "Rarely used after a post has aged" + "Commonly used on posts many years old") + (list "Visible only at the bottom of the parent post" + "Included in the main stream of posts and in the RSS feed alongside actual posts") + (list "Invites any and all feedback, from small compliments to lengthy rebuttals" + "Readers invited to treat their responses as submissions to a publication.") + (list "Usually used by readers" + "Usually used by the original author") + (list "Don’t affect the original post" + "May have properties (e.g. disposition) that change the status and +presentation of the original post") + (list "Moderation (if done) is typically binary: approved or not" + "Moderation may take the form of edits and inline responses."))] + +@subsection{Series} + +A @deftech{series} is a grouping of @tech{articles} into a particular order under a heading. +A series may present its own written content alongside the listing of its articles. + +The page for a series can choose how to display its articles: chronologically, or in an arbitrary +order. It can display articles only, or a mixed listing of articles and notes, like the blog. And it +can choose to display articles in list form, or as excerpts, or in their entirety. + +A series can specify @italic{nouns} to be applied to its articles. + +@subsubsection{Series vs. blog “categories”} + +Typical blogs are not very good at presenting content that may vary a lot in length and style. The +kind of writing I want to experiment with may change a lot from day to day, season to season, decade +to decade. I wanted a single system that could organize and present it all, in a thoughtful, +coherent way, rather than starting a new blog every time I wanted to try writing a different kind of +thing. + +My solution to this was to enrich the idea of “categories”. Rather than being simply labels that you +slap on blog posts, they would be titled collections with their own unique content and way of +presenting articles and notes. In addition, they could pass down certain properties to the posts +they contain, that can be used to give signals to the reader about what they are looking at. + +@tabular[#:sep @hspace[1] + #:style 'boxed + #:row-properties '((bottom-border top)) + (list + (list @bold{Typical Blog Categories/Tags} @bold{Local Yarn @emph{Series}}) + (list "Every article needs to have one" + "Many or most articles won’t have one") + (list "Named with a single word" + "Name with a descriptive title") + (list "Has no content or properties of its own" + "Has its own content and properties") + (list "Broad in scope, few in number" + "Narrow in scope, many in number") + (list "Selected to be relevant for use across the entire lifetime of the site" + "Selected without reference to future creative direction; may be closed after only + a few articles"))] + Index: code-docs/main.scrbl ================================================================== --- code-docs/main.scrbl +++ code-docs/main.scrbl @@ -1,11 +1,14 @@ #lang scribble/manual @; SPDX-License-Identifier: BlueOak-1.0.0 @; This file is licensed under the Blue Oak Model License 1.0.0. -@(require "scribble-helpers.rkt") +@(require "scribble-helpers.rkt" + racket/runtime-path + (for-label racket/base + "../crystalize.rkt")) @title{Local Yarn: source code notes} @author{Joel Dueck} @@ -17,22 +20,42 @@ "pollen/scribblings/pollen.scrbl")], and worked through the tutorials by hand. @margin-note{Note that these pages are heavily interlinked with the central Racket documentation at @tt{docs.racket-lang.org}, which are written and maintained by others. -If you’re browsing these docs from within @italic{The Local Yarn}’s main website, and if you follow -links to other Racket documentation, you’ll find that to @emph{other} sites on those pages will not -work (due to the @ext-link["https://content-security-policy.com"]{content security policy} in -effect when inside a frame). To follow such links, right-click and open the link in a new tab or -window. +Some links from those pages will not work unless you @ext-link["#"]{open this page in its own tab}. +} + +Here’s a rough diagram showing how the @tt{.rkt} modules in this project relate to each other, and +to the Pollen source documents. This is the least complex system I could devise that would @tt{A)} +implement everything I want in my @secref["design-goals"], @tt{B)} cleanly separate dependencies for +print and web output, and @tt{C)} organize an ever-growing collection of hundreds of individual +notes and articles without noticable loss of speed. + +@(define-runtime-path source-diagram "source-diagram.png") +@centered{@responsive-retina-image[source-diagram]} + +The solid-line connections indicate explicit @racket[require] relationships. Dotted arrows generally +indicate implicit exports in the Pollen environment: docs and metas to templates, +@seclink["pollen-rkt"]{@filepath{pollen.rkt}} to source documents and templates. The orange lines +highlight the provision and use of the functions in +@seclink["crystalize-rkt"]{@filepath{crystalize.rkt}}. + +Individual articles, while they are being rendered to HTML pages, save copies of their metadata and +HTML to the SQLite cache. This is done by calling @racket[parse-and-cache-article!] from within +their template. Likewise, series pages cache themselves with a call to @racket[cache-series!] from +within their template. -You may also wish to @ext-link["#"]{open this page in its own tab.}} +Any pages that gather content from multiple articles, such as Series pages and the RSS feed, pull +this content directly from the SQLite cache. This is much faster than trawling through Pollen’s +cached metas of every article looking for matching articles. @local-table-of-contents[] @include-section["tour.scrbl"] -@include-section["overview.scrbl"] +@include-section["design.scrbl"] @include-section["pollen.scrbl"] @; pollen.rkt @include-section["dust.scrbl"] @; dust.rkt @include-section["snippets-html.scrbl"] @; you get the idea @include-section["cache.scrbl"] @include-section["crystalize.scrbl"] + DELETED code-docs/overview.scrbl Index: code-docs/overview.scrbl ================================================================== --- code-docs/overview.scrbl +++ code-docs/overview.scrbl @@ -1,37 +0,0 @@ -#lang scribble/manual - -@; SPDX-License-Identifier: BlueOak-1.0.0 -@; This file is licensed under the Blue Oak Model License 1.0.0. - -@(require "scribble-helpers.rkt" - racket/runtime-path) - -@(require (for-label racket/base)) - -@title{Overview} - -@section{Source Code} - -Here’s a rough diagram showing how the @tt{.rkt} modules in this project relate to each other, and -to the Pollen source documents. - -@(define-runtime-path source-diagram "source-diagram.png") -@centered{@responsive-retina-image[source-diagram]} - -The solid-line connections indicate explicit @racket[require] relationships. Dotted arrows generally -indicate implicit exports in the Pollen environment: docs and metas to templates, -@filepath{pollen.rkt} to source documents and templates. The orange lines highlight the provision -and use of the functions in @filepath{crystalize.rkt}. - -Individual articles, while they are being rendered to HTML pages, save copies of their metadata and -HTML to the SQLite cache. This is done by calling functions in @filepath{crystalize.rkt} from within -the template. - -Any pages that gather content from multiple articles, such as Series pages and the RSS feed, pull -this content directly from the SQLite cache. This is much faster than trawling through Pollen’s -cached metas of every article looking for matching articles. - -This is the least complex system I could devise that can @tt{A)} implement everything I want in my -@wiki{Design and Layout}, @tt{B)} cleanly separate dependencies for print and web output, and -@tt{C)} organize an ever-growing collection of hundreds of individual notes and articles without -noticable loss of speed. Index: code-docs/pollen.scrbl ================================================================== --- code-docs/pollen.scrbl +++ code-docs/pollen.scrbl @@ -4,10 +4,11 @@ @; This file is licensed under the Blue Oak Model License 1.0.0. @(require "scribble-helpers.rkt") @(require (for-label "../pollen.rkt" "../dust.rkt" + "../cache.rkt" "../crystalize.rkt" racket/base racket/contract racket/string txexpr @@ -20,21 +21,22 @@ @defmodule["pollen.rkt" #:packages ()] The file @filepath{pollen.rkt} is implicitly @code{require}d in every template and every @code{#lang pollen} file in the project. It defines the markup for all Pollen documents, and also re-provides -everything provided by @filepath{cache.rkt} and @filepath{crystalize.rkt}. +everything provided by @seclink["cache-rkt"]{@filepath{cache.rkt}} and +@seclink["crystalize-rkt"]{@filepath{crystalize.rkt}}. The @code{setup} module towards the top of the file is used as described in @racketmodname[pollen/setup]. @section{Markup reference} These are the tags that can be used in any of @italic{The Local Yarn}’s Pollen documents (articles, etc). -@defproc[(title [element xexpr?] ...) txexpr?] +@defproc[(title [element xexpr?] ...) txexpr?]{ @margin-note{The @code{title} function is not actually defined in @filepath{pollen.rkt} or anywhere else. In Pollen, any undefined function @tt{title} defaults to @racket[(default-tag-function title)], which is what I want. It is documented here because its presence or absence has side-effects on the display of the article.} @@ -41,47 +43,54 @@ Supplies a title for the document. You can use any otherwise-valid markup within the title tag. Titles are optional; if you don’t specify a title, the article will appear without one. This is a feature! +} -@defproc[(p [element xexpr?] ...) txexpr?] +@defproc[(p [element xexpr?] ...) txexpr?]{ Wrap text in a paragraph. You almost never need to use this tag explicitly; just separate paragraphs by an empty line. Single newlines within a paragraph will be replaced by spaces, allowing you to use @ext-link["https://scott.mn/2014/02/21/semantic_linewrapping/"]{semantic line wrapping}. +} -@defproc[(newthought [element xexpr?] ...) txexpr?] +@defproc[(newthought [element xexpr?] ...) txexpr?]{ An inline style intended for the first few words of the first paragraph in a new section. Applies a “small caps” style to the text. Any paragraph containing a @code{newthought} tag is given extra vertical leading. Rule of thumb: within an article, use either @code{section}/@code{subsection} or @code{newthought} to separate sections of text, but not both. Even better, keep it consistent across articles within a series. -If you just need small caps without affecting the paragraph, use @code{smallcaps}. +If you just need small caps without affecting the paragraph, use @racket[caps]. +} @deftogether[(@defproc[(section [element xexpr?] ...) txexpr?] - @defproc[(subsection [element xexpr?] ...) txexpr?])] + @defproc[(subsection [element xexpr?] ...) txexpr?])]{ Create second- and third-level headings, respectively. This is counting the article's title as the first-level header (even if the current article has no title). -@defproc[(block [element xexpr?] ...) txexpr?] +} + +@defproc[(block [element xexpr?] ...) txexpr?]{ A container for content that should appear grouped together on larger displays. Intended for use in -Series pages, where the template is very minimal. You would want output from -@racket[listing<>-short/articles] to appear inside a @racket[block], but you would want output from -@racket[listing<>-full/articles] to appear outside it (since each article effectively supplies its own +Series pages, where the template is very minimal to allow for more customization. You would want +output from @racket[] to appear inside a @racket[block], but you would want output +from @racket[] to appear outside it (since each article effectively supplies its own block). Only relevant to HTML output. + +} @deftogether[(@defproc[(link [link-id stringish?] [link-text xexpr?]) txexpr?] - @defproc[(url [link-id stringish?] [url string?]) void?])] + @defproc[(url [link-id stringish?] [url string?]) void?])]{ All hyperlinks are specified reference-style. So, to link some text, use the @code{link} tag with an identifier, which can be a string, symbol or number. Elsewhere in the text, use @code{url} with the same identifier to specify the URL: @@ -94,29 +103,35 @@ The @code{url} tag for a given identifier may be placed anywhere in the document, even before it is referenced. If you create a @code{link} for an identifier that has no corresponding @code{url}, a @code{"Missing reference: [link-id]"} message will be substituted for the URL. Conversely, creating a @code{url} that is never referenced will produce no output and no warnings or errors. + +} @deftogether[(@defproc[(figure [image-file string?] [caption xexpr?] ...) txexpr?] - @defproc[(figure-@2x [image-file string?] [caption xexpr?] ...) txexpr?])] + @defproc[(figure-@2x [image-file string?] [caption xexpr?] ...) txexpr?])]{ Insert a block-level image. The @racket[_image-file] should be supplied as a filename only, with no folder names. It is assumed that the image is located inside an @racket[images-folder] within the same folder as the source document. For web output, using @racket[figure-@2x] will produce an image hard-coded to display at half its actual size, or the width of the text block, whichever is smaller. -@defproc[(image-link [image-file string?] [link-text xexpr?] ...) txexpr?] +} + +@defproc[(image-link [image-file string?] [link-text xexpr?] ...) txexpr?]{ Adds a hyperlink to @racket[_image-file], supplied as a filename only with no folder names. It is assumed that the image is located inside an @racket[images-folder] within the same folder as the source document. + +} @deftogether[(@defproc[(fn [fn-id stringish?]) txexpr?] - @defproc[(fndef [fn-id stringish?] [elements xexpr?] ...) txexpr?])] + @defproc[(fndef [fn-id stringish?] [elements xexpr?] ...) txexpr?])]{ As with hyperlinks, footnotes are specified reference-style. In the output, footnotes will be numbered according to the order in which their identifiers are referenced in the source document. Example: @@ -133,14 +148,16 @@ The @code{fndef} for a given id may be placed anywhere in the source document, even before it is referenced. If you create a @code{fn} reference without a corresponding @code{fndef}, a @code{"Missing footnote definition!"} message will be substituted for the footnote text. Conversely, creating a @code{fndef} that is never referenced will produce no output, warning or error. + +} @deftogether[(@defproc[(dialogue [elements xexpr?] ...) txexpr?] @defproc[(say [interlocutor string?] [elements xexpr?] ...) txexpr?] - @defproc[(saylines [interlocutor string?] [elements xexpr?] ...) txexpr?])] + @defproc[(saylines [interlocutor string?] [elements xexpr?] ...) txexpr?])]{ Use these tags together for transcripts of dialogue, chats, screenplays, interviews and so forth. The @racket[saylines] tag is the same as @racket[say] except that within @racket[saylines], linebreaks within paragraphs are preserved. @@ -153,36 +170,39 @@ ◊say["Tavi"]{You also write fiction, or you used to. Do you still?} ◊say["Lorde"]{The thing is, when I write now, it comes out as songs.} } }| -@defproc[(index [#:key key string? ""] [elements xexpr?] ...) txexpr?] +} + +@defproc[(index [#:key key string? ""] [elements xexpr?] ...) txexpr?]{ Creates a bidirectional link between this spot in the document and an entry in the keyword index under @racket[_key]. If @racket[_key] is not supplied, the string contents of @racket[_elements] are -used as the key. +used as the key. Use @tt{!} to split @racket[_key] into a main entry and a subentry. The example below will create two index entries, one under the heading “compassion” and one under -the heading “cats”: +the main heading "cats" and a subheading “stray”: @codeblock|{ #lang pollen “I have a theory which I suspect is rather immoral,” Smiley went on, more lightly. “Each of us has only a quantum of ◊index{compassion}. That if we lavish our concern on every - stray ◊index[#:key "cats"]{cat} we never get to the centre of - things. What do you think of it?” + stray ◊index[#:key "cats!stray"]{cat} we never get to the + centre of things. What do you think of it?” }| + +} @defproc[(note [#:date date-str non-empty-string?] [#:author author string? ""] [#:author-url author-url string? ""] - [#:disposition disp-str string? ""]) txexpr?] + [#:disposition disp-str string? ""]) txexpr?]{ -Add a note to the “Further Notes” section of the article. Notes are like blog comments but are -more rare and powerful; see @wiki{Differences from blogs}. +Add a @tech{note} to the “Further Notes” section of the article. The @code{#:date} attribute is required and must be of the form @tt{YYYY-MM-DD}. The @code{#:author} and @code{#:author-url} attributes can be used to credit notes from other people. If the @code{#:author} attribute is not supplied then the value of @code{default-authorname} @@ -208,25 +228,36 @@ @itemlist[ @item{Avoid defining new footnotes using @code{fndef} inside a @code{note}; these footnotes will be placed into the main footnote section of the article, which is probably not what you want.} ] + +} @defproc[(verse [#:title title string? ""] [#:italic? italic boolean? #f] [element xexpr?] ...) - txexpr?] + txexpr?]{ Typeset contents as poetry, with line breaks preserved and the block centered on the longest line. To set the whole block in italic, use @code{#:italic? #t} — otherwise, use @code{i} within the block. -@defproc[(blockquote [element xexpr?] ...) txexpr?] +If the first element in an article is a @racket[verse] tag with the @racket[#:title] attribute +specified, that title is used as the article’s title if the normal @racket[title] tag is absent. + +} + +@defproc[(blockquote [element xexpr?] ...) txexpr?]{ Surrounds a block quotation. To cite a source, include a @code{footer} tag at the bottom. -@defproc[(blockcode [element xexpr?] ...) txexpr?] +} + +@defproc[(blockcode [element xexpr?] ...) txexpr?]{ Typeset contents as a block of code using a monospace font. Line breaks are preserved. + +} @deftogether[(@defproc[(i [element xexpr?] ...) txexpr?] @defproc[(em [element xexpr?] ...) txexpr?] @defproc[(b [element xexpr?] ...) txexpr?] @defproc[(strong [element xexpr?] ...) txexpr?] @@ -234,17 +265,20 @@ @defproc[(ol [element xexpr?] ...) txexpr?] @defproc[(ul [element xexpr?] ...) txexpr?] @defproc[(item [element xexpr?] ...) txexpr?] @defproc[(sup [element xexpr?] ...) txexpr?] @defproc[(caps [element xexpr?] ...) txexpr?] - @defproc[(code [element xexpr?] ...) txexpr?])] + @defproc[(code [element xexpr?] ...) txexpr?])]{ + Work pretty much how you’d expect. + +} @section{Convenience macros} @defform[(for/s thing-id listofthings result-exprs ...) - #:contracts ([listofthings (listof any/c)])] + #:contracts ([listofthings (listof any/c)])]{ A shorthand form for Pollen’s @code{for/splice} that uses far fewer brackets when you’re only iterating through a single list. @codeblock|{ @@ -253,22 +287,24 @@ ◊for/s[x '(7 8 9)]{Now once for number ◊x} ◊;Above line is shorthand for this one: ◊for/splice[[(x (in-list '(7 8 9)))]]{Now once for number ◊x} }| + +} @section{Defining new tags} I use a couple of macros to define tag functions that automatically branch into other functions depending on the current output target format. This allows me to put the format-specific tag functions in separate files that have separate places in the dependency chain. So if only the HTML -tag functions have changed and not those for PDF, the @filepath{makefile} can ensure only the HTML files are -rebuilt. +tag functions have changed and not those for PDF, the @filepath{makefile} can ensure only the HTML +files are rebuilt. @defproc[#:kind "syntax" (poly-branch-tag (tag-id symbol?)) - (-> txexpr?)] + (-> txexpr?)]{ Defines a new function @racket[_tag-id] which will automatically pass all of its arguments to a function whose name is the value returned by @racket[current-poly-target], followed by a hyphen, followed by @racket[_tag]. So whenever the current output format is @racket['html], the function defined by @racket[(poly-branch-tag _p)] will branch to a function named @racket[html-p]; when the @@ -285,15 +321,19 @@ arguments, see @racket[poly-branch-kwargs-tag]. @margin-note{The thought behind having two macros so similar is that, by cutting out handling for keyword arguments, @racket[poly-branch-tag] could produce simpler and faster code. I have not verified if this intuition is meaningful or correct.} + +} @defproc[#:kind "syntax" (poly-branch-kwargs-tag (tag-id symbol?)) - (-> txexpr?)] + (-> txexpr?)]{ Works just like @racket[poly-branch-tag], but uses Pollen’s @racket[define-tag-function] so that keyword arguments will automatically be parsed as X-expression attributes. Additionally, the branch functions called from the new function must accept exactly two arguments: a list of attributes and a list of elements. + +}